DEV Community

Cover image for How does VuReact compile Vue 3's `<script setup>` into a full React component?
Ryan John
Ryan John

Posted on • Originally published at vureact.top

How does VuReact compile Vue 3's `<script setup>` into a full React component?

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>:

  1. <script setup> itself is a compile-time macro and does not exist at runtime
  2. Its top-level code is extracted into the React component function body
  3. The Vue template is ultimately transformed into the JSX return statement

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>
Enter fullscreen mode Exit fullscreen mode
  • 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;
Enter fullscreen mode Exit fullscreen mode

This transformation shows the core compilation approach for <script setup>:

  • Non-pure UI components are wrapped with memo for performance optimization
  • defineProps is not a runtime call—it is compiled into the component's props parameter
  • defineEmits is compiled into props.onXxx event callbacks
  • Most of the code you write inside script setup directly 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>
Enter fullscreen mode Exit fullscreen mode
  • 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>
  );
});
Enter fullscreen mode Exit fullscreen mode

This demonstrates:

  • Type information from <script setup lang="ts"> is preserved and converted into the component's props type
  • The type parameter of defineProps ends up as the React props type definition
  • Event types from defineEmits are mapped to the callback type signature of onUpdate

Related Links

Top comments (0)