Vue 19: The <script setup> Mental Model
easy⏱ 5 mincoursevue
Real <script setup> SFC syntax
This is genuine, idiomatic modern Vue — the syntax you'll write in every real .vue file. It never runs in this browser sandbox (there's no SFC compiler here), but it's exactly what defaultCode below is hand-compiled from, statement for statement.
<script setup lang='ts'>
import { ref, computed } from 'vue';
const props = defineProps<{ name: string }>();
const emit = defineEmits<{ greeted: [count: number] }>();
const count = ref(0);
const shout = computed(() => props.name.toUpperCase() + '!');
function greet() {
count.value++;
emit('greeted', count.value);
}
// No return -- count, shout, and greet are top-level bindings,
// automatically exposed to the template below.
</script>
<template>
<div class='card'>
<h2>{{ shout }}</h2>
<p>Greeted {{ count }} times</p>
<button @click='greet'>Greet</button>
</div>
</template>
Compiler macros need no import
Notice the SFC never imports defineProps or defineEmits from 'vue' — that would actually be an error. They're globally available ONLY inside <script setup> because the Single-File-Component compiler special-cases them at compile time and erases them from the final JavaScript, replacing them with the plain props/emit machinery you see in defaultCode.
Map the sugar to the real code
Line up the SFC's defineProps<{ name: string }>() with defaultCode's props: { name: String }, and defineEmits<{ greeted: [count: number] }>() with emits: ['greeted']. Click Greet a few times and confirm the parent's console log receives the emitted count.