e Solution
VuReact implements a deterministic compilation pipeline that transforms Vue template nodes into React JSX elements. The strategy relies on mapping directive attributes to JavaScript expressions while preserving the hierarchical structure of the template.
1. Basic Conditional Mapping
The simplest case involves a single v-if directive. The compiler converts this into a ternary expression where the truthy branch renders the element and the falsy branch returns null. This is critical because React renders null as nothing, whereas rendering false or 0 can result in text nodes appearing in the DOM.
Vue Source:
<NotificationBadge v-if="hasUnreadMessages" />
VuReact Output:
{hasUnreadMessages ? <NotificationBadge /> : null}
Rationale: The explicit : null fallback prevents the "falsy value rendering" bug common in manual React migrations.
2. Binary Branching with v-else
When v-else is present, the compiler generates a complete ternary expression. Both branches are mutually exclusive, matching Vue's behavior where only one branch renders at a time.
Vue Source:
<Spinner v-if="isFetching" />
<DataGrid v-else />
VuReact Output:
{isFetching ? <Spinner /> : <DataGrid />}
Rationale: This structure ensures that the React reconciler sees a single expression with two possible outcomes, maintaining the same update cycle as the Vue template.
3. Multi-Branch Chains
Chains involving v-else-if are compiled into nested ternary expressions. The compiler evaluates conditions sequentially, ensuring that once a branch matches, subsequent branches are skipped, exactly mirroring Vue's directive evaluation order.
Vue Source:
<StatusIndicator v-if="connection === 'online'" />
<StatusIndicator v-else-if="connection === 'offline'" />
<StatusIndicator v-else />
VuReact Output:
{connection === 'online'
? <StatusIndicator status="online" />
: connection === 'offline'
? <StatusIndicator status="offline" />
: <StatusIndicator status="unknown" />}
Rationale: Nested ternaries are the idiomatic React pattern for multi-way branching. While deep nesting can impact readability, the compiler optimizes formatting and can extract complex conditions into helper variables if configured, balancing correctness with maintainability.
4. Complex Business Logic Integration
Real-world components combine conditionals with event handlers, loops, and interpolation. VuReact handles these contextually, transforming attributes and expressions simultaneously.
Vue Source:
<div v-if="user.tier === 'pro'">
<h2>Pro Dashboard</h2>
<button @click="exportReport">Export Data</button>
</div>
<div v-else-if="user.tier === 'trial' && trialActive">
<p>Trial expires in {{ daysRemaining }} days</p>
<button @click="upgradePlan">Upgrade Now</button>
</div>
<div v-else>
<p>Access restricted</p>
</div>
VuReact Output:
{user.tier === 'pro' ? (
<div>
<h2>Pro Dashboard</h2>
<button onClick={exportReport}>Export Data</button>
</div>
) : user.tier === 'trial' && trialActive ? (
<div>
<p>Trial expires in {daysRemaining} days</p>
<button onClick={upgradePlan}>Upgrade Now</button>
</div>
) : (
<div>
<p>Access restricted</p>
</div>
)}
Key Transformations:
- Events:
@click maps to onClick, preserving the event handler reference.
- Interpolation:
{{ }} syntax converts to { } JSX expressions.
- Attributes:
class converts to className; for converts to htmlFor.
- Structure: Wrapper
<div> elements are preserved to maintain DOM hierarchy, ensuring CSS selectors and layout behavior remain consistent.
Pitfall Guide
Even with automated compilation, certain patterns require attention to ensure production stability.
1. Deep Nesting Readability
Explanation: Complex v-else-if chains with many branches can result in deeply nested ternaries that are hard to debug.
Fix: Configure the compiler to extract complex conditions into named variables or functions. For chains exceeding 4 branches, consider refactoring the source Vue component to use a computed property that returns a component type, then render that component in React.
2. Falsy Value Rendering
Explanation: In JavaScript, 0, false, and null are falsy. If a condition evaluates to 0, a naive condition && <Element /> pattern will render 0 to the DOM.
Fix: VuReact avoids this by using ternaries with null fallbacks. However, if you manually write React code post-migration, always use condition ? <Element /> : null instead of &&.
3. Key Management in Conditional Loops
Explanation: When v-for appears inside a conditional block, the list items must have stable keys. Migration can sometimes obscure key placement if the loop structure changes.
Fix: Verify that key props are present on mapped elements. Ensure keys are derived from stable identifiers (e.g., item.id) rather than indices, especially if the conditional logic affects list ordering.
4. Event Context Binding
Explanation: Vue automatically binds event handlers to the component instance. React requires explicit binding or arrow functions.
Fix: The compiler handles standard @click to onClick conversion. However, inline arrow functions like @click="() => doSomething(arg)" must be reviewed to ensure arg is correctly scoped in the React closure.
5. Class Name Conflicts
Explanation: Vue's class binding supports arrays and objects natively. React's className expects a string.
Fix: VuReact converts static classes to className. For dynamic classes, the compiler may generate a helper or inline string concatenation. Review dynamic class logic to ensure it matches React's expectations, potentially using a library like clsx for complex cases.
6. Fragment vs. Wrapper Elements
Explanation: Vue templates can have multiple root elements. React requires a single root or a Fragment.
Fix: The compiler wraps multiple roots in a React Fragment <>...</>. Ensure that CSS styles relying on direct child selectors (>) are updated, as the Fragment may alter the DOM depth.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Simple Toggle | Direct Compilation | Ternary output is clean and idiomatic. | Low |
| 3-4 Branches | Direct Compilation | Nested ternaries remain readable. | Low |
| 5+ Branches | Extract Logic | Reduces nesting depth; improves maintainability. | Medium |
| Dynamic Components | Component Map | Avoids large conditional blocks; scales better. | Medium |
| List + Condition | Filter then Map | Separates logic from rendering; optimizes performance. | Low |
Configuration Template
Use this configuration to enable strict compilation modes and customize output formatting for your React project.
import { defineConfig } from 'vureact';
export default defineConfig({
compiler: {
// Target React version for compatibility checks
target: 'react-18',
// Enable strict mode for safer type generation
strictMode: true,
// Formatting options for compiled JSX
formatting: {
// Max branches before suggesting extraction
maxTernaryDepth: 4,
// Indentation style
indent: 2,
},
// Custom transformations
transforms: {
// Map Vue class bindings to clsx usage
classBinding: 'clsx',
// Handle v-model conversion strategy
vModel: 'controlled',
},
},
// Path to Vue source directory
input: './src/vue-components',
// Output directory for React components
output: './src/react-components',
});
Quick Start Guide
- Install Dependencies: Add
vureact and peer dependencies to your project.
npm install vureact react react-dom --save-dev
- Initialize Config: Run the initialization command to generate
vureact.config.ts.
npx vureact init
- Configure Paths: Update the
input and output paths in the config to point to your Vue source and desired React output directories.
- Execute Compilation: Run the build command to transpile components.
npx vureact build
- Validate Output: Review the generated JSX files in the output directory. Run your test suite to ensure behavioral parity with the original Vue components.