VuReact is a compiler toolchain for migrating from Vue to React — and for writing React with Vue 3 syntax. Today, instead of looking at a specific API mapping, we take a broader view: how VuReact compiles Vue's <script setup> into a complete React component.
Before We Start
To keep things concise, this article focuses on the overall compilation output and structural transformation of <script setup>:
-
<script setup>itself is a compile-time macro and does not exist at runtime - Its top-level code is extracted into the React component function body
- The Vue template is ultimately transformed into the JSX
returnstatement
Compilation Mapping
JavaScript: <script setup> to React component function
- Vue
<script setup>
const props = defineProps({
title: String,
count: Number,
});
const emit = defineEmits(['update']);
const handleClick = () => {
emit('update', props.count + 1);
};
</script>
<template>
<button @click="handleClick">
{{ props.title }} - {{ props.count }}
</button>
</template>
- Compiled React
const Comp = memo((props) => {
const handleClick = useCallback(() => {
props.onUpdate?.(props.count + 1);
}, [props.onUpdate, props.count]);
return (
<button onClick={handleClick}>
{props.title} - {props.count}
</button>
);
});
export default Comp;
This transformation shows the core compilation approach for <script setup>:
- Non-pure UI components are wrapped with
memofor performance optimization -
definePropsis not a runtime call—it is compiled into the component'spropsparameter -
defineEmitsis compiled intoprops.onXxxevent callbacks - Most of the code you write inside
script setupdirectly becomes function body logic inside the component - Compile-time static optimization is applied to script code
- The Vue template is compiled into JSX inside the
return (...)
TypeScript: Typed <script setup> full compilation
- Vue
<script setup lang="ts">
const props = defineProps<{
title: string;
count: number;
}>();
const emit = defineEmits<{
(e: 'update', value: number): void;
}>();
const handleClick = () => {
emit('update', props.count + 1);
};
</script>
<template>
<button @click="handleClick">
{{ props.title }} - {{ props.count }}
</button>
</template>
- Compiled React
type ICompProps = {
title: string;
count: number;
onUpdate?: (value: number) => void;
};
const Comp = memo((props: ICompProps) => {
const handleClick = useCallback(() => {
props.onUpdate?.(props.count + 1);
}, [props.onUpdate, props.count]);
return (
<button onClick={handleClick}>
{props.title} - {props.count}
</button>
);
});
This demonstrates:
- Type information from
<script setup lang="ts">is preserved and converted into the component'spropstype - The type parameter of
definePropsends up as the Reactpropstype definition - Event types from
defineEmitsare mapped to the callback type signature ofonUpdate
Top comments (0)