defineProps, defineEmits, and defineExpose in Vue.js
Script Setup Macros
Vue 3's <script setup> provides compiler macros that simplify component declarations. These macros are automatically available — no imports needed.
defineProps
Declare props that the component accepts:
vue
<script setup lang="ts">
// Type-based declaration (recommended)
const props = defineProps<{
title: string
count?: number
items: string[]
user: { name: string; age: number }
}>()
// Access props
console.log(props.title)
</script>With Defaults
vue
<script setup lang="ts">
const props = withDefaults(
defineProps<{
title: string
count?: number
theme?: 'light' | 'dark'
items?: string[]
}>(),
{
count: 0,
theme: 'light',
items: () => [], // Use factory function for objects/arrays
}
)
</script>Runtime Declaration
vue
<script setup>
const props = defineProps({
title: { type: String, required: true },
count: { type: Number, default: 0 },
items: { type: Array, default: () => [] },
})
</script>defineEmits
Declare events the component can emit:
vue
<script setup lang="ts">
const emit = defineEmits<{
change: [value: string]
submit: [data: { name: string }]
close: []
}>()
function handleChange(e: Event) {
emit('change', (e.target as HTMLInputElement).value)
}
function handleSubmit() {
emit('submit', { name: 'Alice' })
}
</script>defineExpose
Expose component methods/properties to parent via template refs:
vue
<!-- ChildComponent.vue -->
<script setup>
import { ref } from 'vue'
const count = ref(0)
function reset() {
count.value = 0
}
function focus() {
// Focus logic
}
// By default, nothing is exposed with <script setup>
// Explicitly expose what parents can access:
defineExpose({ count, reset, focus })
</script>vue
<!-- ParentComponent.vue -->
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const childRef = ref<InstanceType<typeof ChildComponent> | null>(null)
function resetChild() {
childRef.value?.reset() // ✅ Accessible because of defineExpose
childRef.value?.count // ✅ Accessible
}
</script>
<template>
<ChildComponent ref="childRef" />
<button @click="resetChild">Reset Child</button>
</template>defineModel (Vue 3.4+)
Simplifies v-model binding:
vue
<!-- CustomInput.vue -->
<script setup>
const model = defineModel<string>()
// Equivalent to: props.modelValue + emit('update:modelValue')
</script>
<template>
<input v-model="model" />
</template>
<!-- Parent.vue -->
<CustomInput v-model="name" />Named Models
vue
<script setup>
const firstName = defineModel<string>('firstName')
const lastName = defineModel<string>('lastName')
</script>
<!-- <UserForm v-model:firstName="first" v-model:lastName="last" /> -->Summary
| Macro | Purpose | Import needed? |
|---|---|---|
defineProps | Declare component props | No |
defineEmits | Declare component events | No |
defineExpose | Expose API via template ref | No |
defineModel | Two-way binding (v-model) | No |
withDefaults | Default values for props | No |
Important:
These macros are compiler transforms — they don't exist at runtime and don't need imports. Use defineProps with TypeScript generics for type-safe props, defineEmits for typed events, defineExpose to control what parents can access, and defineModel (Vue 3.4+) for simplified v-model.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.