<script setup> is Vue 3's compile-time syntax for using the Composition API inside Single File Components (SFCs). Code inside <script setup> compiles directly into the component's setup() function, removing boilerplate and improving runtime performance.
Every top-level variable, function, and imported component inside <script setup> becomes available in <template> automatically. You no longer need to list components manually or write a giant return { ... } object at the end of the file.
flowchart LR
SFC["script setup + template"] --> Compiler["Vue SFC Compiler"]
Compiler --> Output["Inlined setup() Render Closure"]
<script setup>Vue provides special compile-time macros that work inside <script setup> without needing import statements:
defineProps(): Declares incoming component props with runtime or TypeScript validation.defineEmits(): Declares custom events that the component can emit to its parent.defineExpose(): Chooses which methods or refs a parent template ref can access.defineModel(): Simplifies two-way v-model bindings on custom components (Vue 3.4+).withDefaults(): Supplies default values when using TypeScript type-based defineProps<Props>().<script setup> ExampleThis counter component combines defineProps, defineEmits, reactive state, and an imported child component in a compact .vue file.
<script setup>
import { ref, computed } from 'vue';
import UserBadge from './UserBadge.vue';
const props = defineProps({
initialCount: {
type: Number,
default: 0
}
});
const emit = defineEmits(['update:count', 'reset']);
const count = ref(props.initialCount);
const isPositive = computed(() => count.value > 0);
function handleIncrement() {
count.value++;
emit('update:count', count.value);
}
function handleReset() {
count.value = 0;
emit('reset');
}
</script>
<template>
<div class="setup-card">
<UserBadge name="Senior Developer" />
<p>Counter: {{ count }} (Positive: {{ isPositive }})</p>
<button @click="handleIncrement">Increment</button>
<button @click="handleReset">Reset</button>
</div>
</template>
defineProps or defineEmits: Writing import { defineProps } from 'vue' is unnecessary and triggers compiler warnings. Compiler macros are globally recognized inside <script setup>.if Blocks: Compiler macros like defineProps and defineEmits must stay at the top level of <script setup> and cannot reference local variables declared inside the component.await Without <Suspense>: Writing top-level await inside <script setup> turns the component into an async dependency that requires a parent <Suspense> boundary to render.name or inheritAttrs: false in <script setup>?In Vue 3.3+, call the built-in defineOptions({ name: 'CustomCard', inheritAttrs: false }) macro directly inside <script setup>.
<script setup> run faster than standard setup()?Because <script setup> compiles the template inside the same function scope as the script, compiled render code accesses variables directly without going through an extra proxy object.
<script setup> with a normal <script> block?Yes. If you need to declare module-level exports or run side effects once per module rather than once per component instance, include a standard <script> block alongside <script setup>.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With