# How to Translate Vue 3 setup() Examples to script setup Syntax

> Easily translate Vue 3 setup() examples to script setup syntax. Learn how this compile-time sugar simplifies your code and exposes declarations directly to the template.

- Repository: [Vue/core](https://github.com/vuejs/core)
- Tags: getting-started
- Published: 2026-02-16

---

**The `<script setup>` syntax is compile-time sugar that automatically executes the equivalent of a `setup()` function, eliminating boilerplate return statements and exposing top-level declarations directly to the template.**

Vue 3's Composition API organizes component logic through the `setup()` function, but the newer `<script setup>` syntax streamlines this approach through compiler transformations. Understanding how to map traditional `setup()` patterns to the modern `<script setup>` style allows you to write cleaner, more maintainable components while leveraging the same underlying runtime mechanisms.

## Understanding the Vue 3 setup Function Architecture

The traditional `setup()` function serves as the entry point for Composition API logic in Vue 3 components. Located within the component options object, this function receives `props` and `context` arguments, returning an object whose properties become available to the template.

According to the Vue 3 source code in [`packages/runtime-core/src/component.ts`](https://github.com/vuejs/core/blob/main/packages/runtime-core/src/component.ts), the `setupComponent()` and `setupStatefulComponent()` functions handle the execution of user-defined `setup()` logic. These runtime functions invoke the setup function, process its return value, and bind the exposed properties to the component instance.

## Mapping setup() Logic to script setup

When transitioning from classic `setup()` to `<script setup>`, you must understand how specific patterns translate to compiler macros.

### Handling Props with defineProps

In traditional `setup()`, you access props through the first argument:

```typescript
export default {
  props: { msg: String },
  setup(props) {
    console.log(props.msg)
    return { msg: props.msg }
  }
}

```

With `<script setup>`, use the `defineProps()` compiler macro:

```vue
<script setup lang="ts">
const props = defineProps<{
  msg: string
}>()
console.log(props.msg)
</script>

```

The compiler, specifically in [`packages/compiler-sfc/src/compileScript.ts`](https://github.com/vuejs/core/blob/main/packages/compiler-sfc/src/compileScript.ts), transforms `defineProps()` into the equivalent props declaration and setup function parameter.

### Emitting Events with defineEmits

Classic `setup()` receives an `emit` function via the context object:

```typescript
export default {
  emits: ['update'],
  setup(props, { emit }) {
    const notify = () => emit('update', 'value')
    return { notify }
  }
}

```

In `<script setup>`, declare emits using `defineEmits()`:

```vue
<script setup lang="ts">
const emit = defineEmits<{
  (e: 'update', value: string): void
}>()

function notify() {
  emit('update', 'value')
}
</script>

```

### Exposing Methods with defineExpose

To expose methods to parent components in classic `setup()`, you call the `expose` function:

```typescript
export default {
  setup(props, { expose }) {
    const reset = () => console.log('reset')
    expose({ reset })
    return {}
  }
}

```

With `<script setup>`, use the `defineExpose()` compiler macro:

```vue
<script setup lang="ts">
function reset() {
  console.log('reset')
}

defineExpose({
  reset
})
</script>

```

### Reactive State and Lifecycle Hooks

Both syntaxes use identical Composition API utilities for state and side effects. In [`packages/runtime-core/src/component.ts`](https://github.com/vuejs/core/blob/main/packages/runtime-core/src/component.ts), the runtime handles `ref`, `reactive`, `computed`, `watch`, and lifecycle hooks like `onMounted` identically regardless of whether they originate from manual `setup()` or compiled `<script setup>`.

```vue
<script setup>
import { ref, watch, onMounted } from 'vue'

const name = ref('Vue')

watch(name, (newVal, oldVal) => {
  console.log(`Name changed from ${oldVal} to ${newVal}`)
})

onMounted(() => {
  console.log('Component is mounted')
})
</script>

```

## Compiler Implementation Details

The transformation from `<script setup>` to executable code occurs in [`packages/compiler-sfc/src/compileScript.ts`](https://github.com/vuejs/core/blob/main/packages/compiler-sfc/src/compileScript.ts). This module parses the SFC, identifies compiler macros like `defineProps` and `defineEmits`, and generates a virtual `setup()` function that the runtime executes.

During runtime, [`packages/runtime-core/src/component.ts`](https://github.com/vuejs/core/blob/main/packages/runtime-core/src/component.ts) contains `setupComponent()` and `setupStatefulComponent()`, which invoke the generated setup function and bind the returned context to the component instance. The vnode preparation occurs in [`packages/runtime-core/src/vnode.ts`](https://github.com/vuejs/core/blob/main/packages/runtime-core/src/vnode.ts) via `setupBlock()`.

## Complete Code Examples

### Classic setup() vs script setup

**Classic `setup()` approach:**

```typescript
export default {
  props: {
    msg: String
  },
  emits: ['update'],
  setup(props, { emit, expose }) {
    const count = ref(0)

    const inc = () => {
      count.value++
      emit('update', count.value)
    }

    expose({ reset: () => (count.value = 0) })

    return { count, inc }
  }
}

```

**Equivalent `<script setup>`:**

```vue
<script setup lang="ts">
import { ref } from 'vue'

const props = defineProps<{
  msg: string
}>()

const emit = defineEmits<{
  (e: 'update', value: number): void
}>()

const count = ref(0)

function inc() {
  count.value++
  emit('update', count.value)
}

defineExpose({
  reset() {
    count.value = 0
  }
})
</script>

<template>
  <div>
    <p>{{ props.msg }} – {{ count }}</p>
    <button @click="inc">Increase</button>
  </div>
</template>

```

### Using withDefaults for default prop values

```vue
<script setup lang="ts">
const props = withDefaults(
  defineProps<{
    title?: string
    size?: 'sm' | 'md' | 'lg'
  }>(),
  {
    title: 'Untitled',
    size: 'md'
  }
)

function capitalize(s: string) {
  return s.charAt(0).toUpperCase() + s.slice(1)
}
</script>

<template>
  <h2>{{ capitalize(props.title) }}</h2>
  <p>Size: {{ props.size }}</p>
</template>

```

## Summary

- The `<script setup>` syntax is compile-time sugar that eliminates boilerplate from the traditional Vue 3 setup function.
- Use `defineProps()` to declare typed props, `defineEmits()` for events, and `defineExpose()` to expose methods to parent components.
- Top-level declarations in `<script setup>` are automatically available in the template without requiring a return statement.
- The compiler transforms `<script setup>` into a standard `setup()` function in [`packages/compiler-sfc/src/compileScript.ts`](https://github.com/vuejs/core/blob/main/packages/compiler-sfc/src/compileScript.ts), while the runtime executes it via `setupComponent()` in [`packages/runtime-core/src/component.ts`](https://github.com/vuejs/core/blob/main/packages/runtime-core/src/component.ts).
- All Composition API utilities (`ref`, `reactive`, `watch`, `onMounted`, etc.) work identically in both syntaxes.

## Frequently Asked Questions

### What is the difference between setup() and script setup?

The `setup()` function is a component option that receives `props` and `context` arguments and must return an object to expose bindings to the template. The `<script setup>` syntax is compile-time sugar that automatically generates the `setup()` function for you, removing the need for `export default`, manual return statements, and the `props`/`context` arguments. According to the Vue 3 source code in [`packages/compiler-sfc/src/compileScript.ts`](https://github.com/vuejs/core/blob/main/packages/compiler-sfc/src/compileScript.ts), the compiler parses top-level declarations and transforms them into the equivalent setup function body.

### How does defineProps work in script setup?

`defineProps` is a compiler macro that declares component props within `<script setup>`. It accepts either an object literal for runtime declaration or TypeScript type parameters for type-based declaration. When the compiler processes [`packages/compiler-sfc/src/compileScript.ts`](https://github.com/vuejs/core/blob/main/packages/compiler-sfc/src/compileScript.ts), it extracts the prop definitions and generates the appropriate `props` option for the component while also making the props available as local variables in the setup scope. This eliminates the need to access props via the first argument of a `setup()` function.

### Can I use script setup with TypeScript?

Yes, `<script setup>` offers excellent TypeScript support. You can use type parameters with `defineProps<{ msg: string }>()` and `defineEmits<{ (e: 'update', value: number): void }>()` for full type inference. The `withDefaults()` helper allows you to provide default values while maintaining type safety. The compiler in [`packages/compiler-sfc/src/compileScript.ts`](https://github.com/vuejs/core/blob/main/packages/compiler-sfc/src/compileScript.ts) preserves TypeScript types during transformation, and the runtime in [`packages/runtime-core/src/component.ts`](https://github.com/vuejs/core/blob/main/packages/runtime-core/src/component.ts) maintains type compatibility when executing the generated setup logic.

### Where is the setup logic compiled in Vue 3 source code?

The compilation of `<script setup>` occurs in [`packages/compiler-sfc/src/compileScript.ts`](https://github.com/vuejs/core/blob/main/packages/compiler-sfc/src/compileScript.ts), where the `compileScript()` function parses the SFC, identifies compiler macros like `defineProps` and `defineEmits`, and generates the equivalent `setup()` function code. At runtime, the execution happens in [`packages/runtime-core/src/component.ts`](https://github.com/vuejs/core/blob/main/packages/runtime-core/src/component.ts) within `setupComponent()` and `setupStatefulComponent()`, which invoke the generated setup function and bind the returned context to the component instance. The vnode preparation occurs in [`packages/runtime-core/src/vnode.ts`](https://github.com/vuejs/core/blob/main/packages/runtime-core/src/vnode.ts) via `setupBlock()`.