Skip to main content
Practice Problems

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

MacroPurposeImport needed?
definePropsDeclare component propsNo
defineEmitsDeclare component eventsNo
defineExposeExpose API via template refNo
defineModelTwo-way binding (v-model)No
withDefaultsDefault values for propsNo

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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems