setup & `<script setup>`

`<script setup>` Is Modern Vue

setup & `<script setup>`

`<script setup>` is compile-time sugar over the setup() function — top-level bindings become available in the template automatically.

4 min read Level 2/5 #vue#composition#setup
What you'll learn
  • Use `<script setup>`
  • Compare to setup() function form
  • Mix with regular `<script>` if needed

<script setup> is the modern way to author Vue components. Anything declared at the top level — refs, functions, imports — is automatically usable in the template, with no return block needed.

<script setup> — The Modern Way

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

const msg = ref('Hello')
const shout = () => msg.value = msg.value.toUpperCase()
</script>

<template>
  <p>{{ msg }}</p>
  <button @click="shout">Shout</button>
</template>

The Long Form — setup()

<script setup> compiles down to a setup() function. You’d only write this form for special cases like render functions or dynamic exports.

<script lang="ts">
import { ref, defineComponent } from 'vue'

export default defineComponent({
  setup() {
    const msg = ref('Hello')
    const shout = () => msg.value = msg.value.toUpperCase()
    return { msg, shout }  // explicit return
  },
})
</script>

The verbosity is what <script setup> removes.

Mixing With a Regular Script

You sometimes want module-level code (a non-reactive export, types, options like name). Add a second <script> block alongside the setup one:

<script lang="ts">
export default { name: 'MyComponent', inheritAttrs: false }
</script>

<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>

Both blocks compile together; setup-block bindings remain the component’s reactive state.

Why Prefer setup

  • Less boilerplate — no return statement.
  • Better TS inference for props, emits, slots.
  • Top-level await works.
  • Compile-time macros: defineProps, defineEmits, defineModel.
Props With defineProps →