Under The Hood
frontend vue Vue 3.2+ (script setup stable)

Vue <script setup>: what the compiler actually does

Last updated
Prerequisites:
Vue SFC basics
Familiarity with the Options API vs Composition API
  • vue
  • script-setup
  • compiler
  • sfc
  • ast
  • macros

Read at your depth

The practical view

<script setup> lets you write Composition API code with top-level bindings automatically exposed to the template: defineProps, defineEmits, defineExpose, and useSlots/useAttrs are compiler macros (no import needed). Variables, imports, and functions declared at top level are directly usable in <template>. Compared to the Options API, there is no this — props and state are just variables. The compiler also enables defineModel (v-model sugar), and with it the component's public interface is declared via macros rather than an options object.

The same idea in other frameworks

react equivalent
// React: props arrive as a function argument
function Card({ title, count }) {
  return <p>{title} · {count}</p>;
}

Legacy vs modern

Options API boilerplate vs compiled <script setup>

The Options API wires everything through string keys and this; <script setup> compiles top-level bindings directly into a setup function, removing the ceremony.

before → after
Options API
export default {
  props: ['title', 'count'],
  emits: ['tick'],
  data() { return { open: false }; },
  methods: { toggle() { this.open = !this.open; this.$emit('tick'); } }
}
script setup
<script setup lang="ts">
const props = defineProps<{ title: string; count: number }>();
const emit = defineEmits<{ tick: [open: boolean] }>();
const open = ref(false);
</script>

Interview gotchas

Context

This question separates candidates who know the macros are compiler magic from those who assume they are normal imports from 'vue'.

The mechanical answer

They are compiler macros: the SFC compiler detects the calls during compileScript, verifies they are used at the top level of <script setup> (not inside functions or conditionals), and removes them from the output while generating the corresponding runtime props/emits bindings. That is why there is no import — importing would break the static analysis. The top-level constraint exists because the compiler needs a static, unconditional declaration to generate the setup context; a conditional defineProps would make the component contract unknowable at compile time. TypeScript's macro type-checking is provided by the vue language tools injecting ambient declarations.

Trap

The naive answer is 'they are imported from vue'. They are not, and importing defineProps explicitly actually triggers a lint/compiler warning in some setups. The deeper trap: claiming you can call defineProps inside a function to 'conditionally declare props' — the compiler rejects it, because the SFC contract must be static. Interviewers also like when you mention withDefaults as the typed-defaults companion that compiles down to the runtime default assignment.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.