Skip to content

Quick Start

Help developers get started in the shortest time and complete the compilation and conversion of a simple Vue project to a React project.

🎥 Check out the 2-minute demo below for a quick overview.

Overview

  1. What conventions the input SFC must follow for stable conversion
  2. What the output directory structure looks like
  3. The semantic correspondence between the output TSX and the original SFC
  4. The compiler automatically analyzes and appends React hook dependencies, eliminating the need for manual management

Step 0: Prepare a Vite + Vue Project

  • Initialize a standard Vue + TS project using Vite:
bash
npx create-vite@latest vue-app --template vue-ts
  • When prompted Install with npm and start now?, select No.

  • You will see a project directory structure similar to the following:

txt
vue-app/
├─ public/
├─ src/
│  ├─ assets/
│  ├─ components/
│  │  └─ HelloWorld.vue
│  ├─ App.vue
│  ├─ main.ts
│  └─ style.css
├─ index.html
├─ package.json
├─ tsconfig.json
├─ vite.config.ts
└─ ...

Step 1: Installation

  • Navigate to the directory and install project dependencies:
bash
cd vue-app
npm install
  • Install the VuReact compiler core:
bash
npm install -D @vureact/compiler-core

Step 2: Configure the Compiler

Create a vureact.config.ts file in the vue-app directory:

ts
// vue-app/vureact.config.ts
import { defineConfig } from '@vureact/compiler-core';

export default defineConfig({
  // Input path containing Vue files to compile; single file 'xxx.vue' is also supported
  input: './src',

  // Exclude Vue entry files to avoid semantic conflicts
  exclude: ['src/main.ts'],

  output: {
    // Workspace directory for storing compilation artifacts and cache
    workspace: '.vureact',

    // Output directory name
    outDir: 'react-app',

    // Automatically initialize a Vite React environment
    bootstrapVite: true,
  },
});

In fact, besides exclude which must be specified manually, all other options use the default values shown in the example configuration—no additional configuration is needed.

Step 3: Write the Vue Component

3.1 Implement a Simple Counter

Replace the original HelloWorld.vue with the counter component code:

html
<!-- src/components/HelloWorld.vue -->
<template>
  <section class="counter-card">
    <h1>{{ props.title }}</h1>
    <h2>
      <span class="vureact">VuReact</span>

      <span class="vue">Vue</span>
      🟰
      <span class="react">React</span>
      ({{ count }})
    </h2>
    <p>{{ title }}</p>
    <button @click="increment">+1</button>
    <button @click="methods.decrease">-1</button>
  </section>
</template>

<script setup lang="ts">
// @vr-name: HelloWorld
import { computed, ref, watch } from 'vue';

// In addition to the special comment at the top, component names can also be defined using macros
// defineOptions({ name: 'HelloWorld' });

// defineProps must be used to define props
const props = defineProps<{ title?: string }>();

// defineEmits must be used to define emits
const emits = defineEmits<{
  (e: 'update', value: number): void;
}>();

const step = ref(1);
const count = ref(0);
const title = computed(() => `阶数:x${step.value}`);

const increment = () => {
  count.value += step.value;
  emits('update', count.value);
};

const methods = {
  decrease() {
    count.value -= step.value;
    emits('update', count.value);
  },
};

watch(count, (newVal) => {
  step.value = Math.floor(newVal / 10) || 1;
});
</script>

<!-- VuReact supports processing Less and Sass -->
<style scoped>
.counter-card {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 12px;
  .vureact {
    color: #9932cc;
  }
  .vue {
    color: #42b883;
  }
  .react {
    color: #61dafb;
  }
}
</style>

3.2 Update the App.vue to Use the HelloWorld Component

vue
<!-- src/App.vue -->
<script setup lang="ts">
// @vr-name: App
import HelloWorld from './components/HelloWorld.vue';
</script>

<template>
  <HelloWorld
    title="Counter"
    @update="(v) => {console.log(v)}"
  />
</template>

Step 4: Compile to a React Project

Method 1: Using npx

Run the following in the vue-app directory:

bash
# Full/incremental compilation
npx vureact build

# Or compile in watch mode
npx vureact watch

Method 2: Using npm scripts

Add the following scripts to your package.json:

json
"scripts": {
  "vr:build": "vureact build",
  "vr:watch": "vureact watch"
}
bash
npm run vr:build

Step 5: View the Output Directory Structure

Output to the vue-app/.vureact workspace directory (illustrative):

txt
vue-app/
├── .vureact/              # Workspace (generated by compilation)
│   ├── cache/             # Compilation cache
│   ├── react-app/         # Generated Vite + React project
│   │   ├── src/
│   │   │   ├── components/
│   │   │   │   ├── HelloWorld.tsx
│   │   │   │   └── HelloWorld-[hash].css
│   │   │   ├── App.tsx
│   │   │   ├── index.css
│   │   │   ├── main.tsx
│   │   │   └── style.css
│   │   └── package.json
│   │   └── tsconfig.json
│   │   └── vite.config.ts
│   │   └── ...
│   │
├── src/                   # Original Vue source code
├── ...
└── vureact.config.js      # VuReact configuration file

Step 6: Run the React Application

  • Navigate into the react-app directory:
bash
cd .vureact/react-app
  • Install dependencies:
bash
npm run install
  • Start the project:
bash
npm run dev

Note: When you open the page, you may notice style differences from the Vue version. This is because Vite's default React initialization injects its own index.css into main.tsx. You can adjust it manually.

If you encounter any issues, refer to the FAQ section.

Step 7: Compare the Generated Output

Below is a formatted typical output (slightly simplified for explanation; actual hashes and property names depend on your local build):

tsx
import { useComputed, useVRef, useWatch } from '@vureact/runtime-core';
import { memo, useCallback, useMemo } from 'react';
import './HelloWorld-ebf8d8dc.css';

// VuReact automatically generates based on defineProps and defineEmits
export type IHelloWorldProps = {
  title?: string;
} & {
  onUpdate?: (value: number) => void;
};

// Automatically use memo to optimize the component
const HelloWorld = memo((props: IHelloWorldProps) => {
  // ref/computed converted to equivalent adapter API
  const step = useVRef(1);
  const count = useVRef(0);
  const title = useComputed(() => `阶数:x${step.value}`);

  // Automatically analyze dependencies in top-level arrow functions and append useCallback optimization
  const increment = useCallback(() => {
    count.value += step.value;
    props.onUpdate?.(count.value); // emits 转换
  }, [count.value, step.value, props.onUpdate]);

  // Automatically analyze dependencies in top-level variables and append useMemo optimization
  const methods = useMemo(
    () => ({
      decrease() {
        count.value -= step.value;
        props.onUpdate?.(count.value);
      },
    }),
    [count.value, step.value, props.onUpdate],
  );
  
  // watch converted to equivalent adapter API
  useWatch(count, (newVal) => {
    step.value = Math.floor(newVal / 10) || 1;
  }); 

  return (
    <>
      <section className="counter-card" data-css-ebf8d8dc>
        <h1 data-css-ebf8d8dc>{props.title}</h1>
        <h2 data-css-ebf8d8dc>
          <span class="vureact" data-css-ebf8d8dc>VuReact</span>

          <span class="vue" data-css-ebf8d8dc>Vue</span>
          🟰
          <span class="react" data-css-ebf8d8dc>React</span>
          ({count.value})
        </h2>
        {/* Automatically append .value to ref access */}
        <p data-css-ebf8d8dc>{title.value}</p>
        <button onClick={increment} data-css-ebf8d8dc>
          +1
        </button>
        <button onClick={methods.decrease} data-css-ebf8d8dc>
          -1
        </button>
      </section>
    </>
  );
});

// Automatically keep component export
export default Counter;

CSS file content:

css
.counter-card[data-css-abc123] {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 12px;
}

Key Observations

  1. // @vr-name: Counter — This special comment defines the component name
  2. defineProps and defineEmits are converted to typed TypeScript component interfaces
  3. Non-pure UI display components are automatically wrapped with memo
  4. ref/computed/watch are converted to equivalent runtime adapter APIs (useVRef/useComputed/useWatch)
  5. Template event callbacks generate React-compatible event handlers like onClick
  6. Top-level arrow functions are automatically analyzed for dependencies and wrapped with useCallback
  7. Top-level variable declarations are automatically analyzed for dependencies and wrapped with useMemo
  8. .value is appended to ref/computed values used in JSX
  9. scoped styles generate hashed CSS files and apply scoped attributes to elements

Summary

Through the steps above, you have completed the full compilation process from a Vue SFC project to a React project. Let's recap the entire flow:

  1. Initialize the project: Create a standard Vue + TS project using Vite
  2. Install the compiler: Add the @vureact/compiler-core dependency
  3. Write the configuration: Use vureact.config.ts to specify input, exclusion, and output rules
  4. Write the component: Follow conventions (@vr-name comment, defineProps/defineEmits macros) when authoring SFCs
  5. Execute compilation: Use the CLI command for one-click conversion
  6. Run the output: Start the generated React project directly and verify the results

VuReact provides the following core conversion capabilities:

  • Vue template syntax → React JSX (v-if/v-slot/v-model/<slot> etc.)
  • Composition API → React Hooks (refuseVRef, computeduseComputed)
  • Reactive dependency analysis → Automatic injection of useCallback/useMemo dependency arrays
  • Component communication → Props type inference + event callback mapping
  • Style processing → Scoped CSS / CSS Modules / preprocessor support all-in-one compilation

Next Steps

After completing the Getting Started guide, we recommend continuing with the following sections:

Released under the MIT License