defineModel() Macro: Synthetic Props and Update Events
- Last updated
- Prerequisites:
- Vue v-model basics
- script-setup compiler macros
- vue
- defineModel
- v-model
- two-way-binding
- macros
- script-setup
Read at your depth
The practical view
`defineModel()` is a compiler macro in `<script setup>` that simplifies two-way bindings. `const model = defineModel();` replaces the boilerplate of declaring `defineProps(['modelValue'])` and `defineEmits(['update:modelValue'])`. You can mutate `model.value = 'newVal'` directly in the child, and Vue automatically emits the update event to synchronize the parent's `v-model`.
The same idea in other frameworks
Controlled props pattern: passing `value` and `onChange` callbacks.Angular 17.2+ `model()` two-way signal binding primitive.`const model = defineModel()` with parent `v-model="val"`.Svelte 5 `$bindable()` inside `$props()` destructuring.Legacy vs modern
Manual props/emits v-model Boilerplate vs defineModel()
Manual `v-model` requires declaring matching props and emits; `defineModel()` creates a clean, mutable ref.
const props = defineProps(['modelValue']); const emit = defineEmits(['update:modelValue']); // ❌ Verboseconst model = defineModel({ type: String, default: '' }); // ✅ NEW: Direct mutable refInterview gotchas
Context
Interviewers test your knowledge of advanced `v-model` features in Vue 3.4+.
The mechanical answer
For named models (e.g. `v-model:title="postTitle"`), pass the name as the first argument: `const title = defineModel('title')`. For custom modifiers (e.g. `v-model.capitalize="text"`), destructure the returned tuple: `const [model, modifiers] = defineModel({ set(value) { if (modifiers.capitalize) return value.toUpperCase(); return value; } })`.
Trap
Do not declare `defineProps(['title'])` and `defineModel('title')` in the same component; declaring both creates conflicting duplicate prop declarations.
Context
Checks understanding of compiler macro execution boundaries.
The mechanical answer
No. `defineModel()` is a compiler macro, not a runtime function. It must be called at the top level of `<script setup>` so the compiler can statically analyze the component's public props and emits contract.
Trap
Placing `defineModel()` inside a lifecycle hook or conditional `if` block will cause a compile-time error.