evelopment dependency in your existing Vue 3 project.
2. Define a configuration file that specifies input directories, exclusion rules, and output workspace structure.
3. Run an incremental build targeting a single component or a specific directory to validate transformation accuracy.
4. Switch to watch mode to maintain synchronization between Vue source and React output during active development.
5. Validate the generated workspace using standard React tooling, testing suites, and linting rules.
New Code Example: Inventory Dashboard Component
To demonstrate the workflow, consider an InventoryDashboard component that manages stock levels, category filtering, and real-time updates. The Vue input uses standard Composition API patterns. The manual React equivalent shows the explicit optimization burden. The compiler output demonstrates how the tool abstracts maintenance decisions while preserving pure React semantics.
Vue Input (Authoring Source)
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
const props = defineProps<{ warehouseId: string; initialStock?: number }>();
const emits = defineEmits<{ (e: 'stockUpdate', payload: { id: string; level: number }) }>();
const selectedCategory = ref('electronics');
const stockLevel = ref(props.initialStock ?? 0);
const categoryMultiplier = computed(() => {
return selectedCategory.value === 'electronics' ? 1.5 : 1.0;
});
const adjustedStock = computed(() => Math.floor(stockLevel.value * categoryMultiplier.value));
const updateStock = (delta: number) => {
stockLevel.value += delta;
emits('stockUpdate', { id: props.warehouseId, level: adjustedStock.value });
};
watch(selectedCategory, () => {
stockLevel.value = Math.max(0, stockLevel.value - 5);
});
</script>
Manual React Equivalent (Handwritten)
import { useState, useMemo, useCallback, useEffect } from 'react';
interface InventoryProps {
warehouseId: string;
initialStock?: number;
onStockUpdate?: (payload: { id: string; level: number }) => void;
}
const InventoryDashboard = ({ warehouseId, initialStock = 0, onStockUpdate }: InventoryProps) => {
const [selectedCategory, setSelectedCategory] = useState('electronics');
const [stockLevel, setStockLevel] = useState(initialStock);
const categoryMultiplier = useMemo(() => {
return selectedCategory === 'electronics' ? 1.5 : 1.0;
}, [selectedCategory]);
const adjustedStock = useMemo(() => Math.floor(stockLevel * categoryMultiplier), [stockLevel, categoryMultiplier]);
const updateStock = useCallback((delta: number) => {
setStockLevel((prev) => {
const next = prev + delta;
onStockUpdate?.({ id: warehouseId, level: Math.floor(next * categoryMultiplier) });
return next;
});
}, [warehouseId, categoryMultiplier, onStockUpdate]);
useEffect(() => {
setStockLevel((prev) => Math.max(0, prev - 5));
}, [selectedCategory]);
return null; // JSX omitted for brevity
};
Compiler Output (Generated Artifact)
import { memo } from 'react';
import { useVRef, useComputed, useWatch } from '@vureact/runtime-helpers';
interface IInventoryDashboardProps {
warehouseId: string;
initialStock?: number;
onStockUpdate?: (payload: { id: string; level: number }) => void;
}
const InventoryDashboard = memo((props: IInventoryDashboardProps) => {
const selectedCategory = useVRef('electronics');
const stockLevel = useVRef(props.initialStock ?? 0);
const categoryMultiplier = useComputed(() => {
return selectedCategory.value === 'electronics' ? 1.5 : 1.0;
});
const adjustedStock = useComputed(() => Math.floor(stockLevel.value * categoryMultiplier.value));
const updateStock = (delta: number) => {
stockLevel.value += delta;
props.onStockUpdate?.({ id: props.warehouseId, level: adjustedStock.value });
};
useWatch(selectedCategory, () => {
stockLevel.value = Math.max(0, stockLevel.value - 5);
});
return null; // JSX omitted for brevity
});
export default InventoryDashboard;
Architecture Decisions and Rationale
The compiler avoids runtime bridges because they introduce hidden performance costs, debugging complexity, and framework coupling. By generating pure React, the output integrates seamlessly with existing CI pipelines, testing frameworks, and code review standards. The convention-driven approach ensures predictable transformation rules, preventing the black-box behavior that plagues many automated codemods.
The use of lightweight helper hooks (useVRef, useComputed, useWatch) in the output is intentional. These helpers do not inject Vue's reactivity engine; they provide a stable interface for the compiler to map Vue's implicit reactivity to React's explicit dependency model. The helpers are tree-shakeable, type-safe, and fully compatible with React's rendering lifecycle. This design choice ensures that the output remains maintainable by developers who may not have authored the original Vue code.
TypeScript inference is preserved through static analysis of Vue's defineProps and defineEmits. The compiler generates strict interface definitions, eliminating the need for manual type synchronization between source and output. This reduces type drift during incremental migration and ensures that downstream React consumers receive accurate prop contracts.
Pitfall Guide
Migration workflows fail when teams treat the compiler as a magic translator rather than a structured engineering tool. The following pitfalls represent the most common production failures, along with proven mitigation strategies.
1. Assuming Zero-Convention Conversion
Vue supports dynamic components, keep-alive, custom directives, and plugin-specific reactivity patterns that lack direct React equivalents. The compiler will either skip these patterns or generate fallback code that may not match expected behavior.
Fix: Audit your Vue codebase for framework-specific features before migration. Replace custom directives with standard React patterns, convert keep-alive logic to React state management, and document any manual overrides required for dynamic components.
2. Ignoring Build Exclusions
Compiling entry points, Vue Router configurations, or plugin initialization files produces broken React artifacts. These files contain framework-specific mounting logic that does not translate to React's component tree.
Fix: Always configure explicit exclusion rules for main.ts, router files, store initializers, and plugin registration modules. Treat these as migration boundaries rather than compilation targets.
3. Over-Memoizing in the Source
Vue developers sometimes wrap simple values in computed or watch out of habit, even when reactivity is unnecessary. The compiler will faithfully translate these into React memoization hooks, bloating the output with redundant dependency tracking.
Fix: Simplify Vue source code before compilation. Remove unnecessary computed wrappers around static values, replace watch with direct event handlers where possible, and rely on Vue's native reactivity for straightforward state updates.
4. Neglecting Style Scoping Translation
Vue's <style scoped> uses attribute-based CSS isolation. React ecosystems typically rely on CSS modules, styled-components, or utility frameworks. The compiler generates standard CSS, but scoping behavior may not match expectations.
Fix: Configure the compiler's style transformation pipeline to match your React styling strategy. Map scoped attributes to CSS module class names, or adopt a utility-first framework that handles isolation automatically. Validate visual regression after each compilation batch.
5. Treating Output as Immutable
The generated React code is meant to be maintained, not frozen. Teams that treat the output as a black box eventually face drift when manual React optimizations are applied directly to the compiled files, breaking synchronization with the Vue source.
Fix: Establish a strict workflow where Vue remains the source of truth. Apply React-specific optimizations only through compiler configuration or post-processing scripts, never by manually editing generated files. Use version control to track compilation deltas.
6. Dependency Array Drift in Manual Overrides
When developers manually tweak generated components, they often introduce stale closures or missing dependencies. The compiler's static analysis cannot track manual changes, leading to inconsistent behavior between source and output.
Fix: Implement linting rules that flag manual dependency array modifications. Use automated testing to verify that generated components match Vue source behavior. Reserve manual React optimizations for components explicitly excluded from compilation.
7. Skipping Incremental Validation
Compiling an entire codebase at once masks transformation errors, style mismatches, and type inconsistencies. Teams that attempt full migration in a single pass often discover critical failures late in the process.
Fix: Adopt a component-by-component migration strategy. Validate each compiled artifact against its Vue source using unit tests, visual regression checks, and performance profiling. Expand compilation scope only after each batch passes validation.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Team comfortable in Vue, needs React output | Compiler-assisted migration | Preserves authoring velocity, reduces manual optimization burden | Low initial setup, moderate long-term maintenance |
| Full React rewrite required, no Vue expertise | Manual React development | Direct control over architecture, no framework translation overhead | High initial cost, high maintenance risk |
| Runtime compatibility needed, gradual transition | Runtime bridge (e.g., Vue-in-React wrappers) | Allows coexistence, minimal code changes | High runtime overhead, debugging complexity |
| Legacy Vue codebase, strict performance requirements | Selective compilation + manual React | Targets high-value components, optimizes critical paths | Moderate setup, high precision, scalable |
Configuration Template
import { defineConfig } from '@vureact/compiler-core';
export default defineConfig({
// Source directory or specific component path
input: './src/components',
// Exclude framework entry points and plugin initializers
exclude: [
'src/main.ts',
'src/router/**',
'src/store/**',
'src/plugins/**'
],
// Output workspace configuration
output: {
workspace: '.vureact',
outDir: 'react-app',
bootstrapVite: true,
styleStrategy: 'css-modules', // Options: 'scoped', 'css-modules', 'tailwind'
strictTypes: true,
treeShakeHelpers: true
},
// Transformation rules
transform: {
preserveComments: false,
inlineComputed: true,
autoMemoizeCallbacks: true,
emitNaming: 'camelCase'
}
});
Quick Start Guide
- Install the compiler: Run
npm install -D @vureact/compiler-core in your Vue 3 project root.
- Create configuration: Add
vureact.config.ts using the template above, adjusting input and exclude paths to match your project structure.
- Run initial build: Execute
npx vureact build to generate the React workspace under .vureact/react-app.
- Validate output: Navigate to the generated directory, run
npm install, then npm run dev to verify the compiled components render correctly.
- Enable watch mode: Run
npx vureact watch to maintain synchronization between Vue source and React output during development.