Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/declare-emits-for-documentation.md
1---2title: Always Declare Emits for Documentation and Validation3impact: MEDIUM4impactDescription: Undeclared emits cause warnings, break TypeScript inference, and prevent event validation5type: best-practice6tags: [vue3, emits, defineEmits, component-events, typescript, documentation]7---89# Always Declare Emits for Documentation and Validation1011**Impact: MEDIUM** - Declaring emitted events with `defineEmits()` or the `emits` option is technically optional, but strongly recommended. Without declarations, Vue shows runtime warnings, TypeScript can't infer event types, and you lose the ability to validate event payloads.1213Declared emits also serve as self-documentation, making it immediately clear what events a component can emit.1415## Task Checklist1617- [ ] Use `defineEmits()` in `<script setup>` to declare all events18- [ ] Use `emits` option when not using `<script setup>`19- [ ] Add TypeScript types for event payloads20- [ ] Consider adding validation functions for complex payloads21- [ ] Document the purpose of each event2223## The Warning2425When you emit without declaring:2627```vue28<script setup>29// No defineEmits declaration30function handleClick() {31emit('select', item) // Vue warns in dev mode32}33</script>34```3536Vue warns:37```38[Vue warn]: Component emitted event "select" but it is neither declared39in the emits option nor as an "onSelect" prop.40```4142## Basic Declaration4344**Correct - Array syntax:**45```vue46<script setup>47const emit = defineEmits(['submit', 'cancel', 'update'])4849function handleSubmit() {50emit('submit', formData)51}5253function handleCancel() {54emit('cancel')55}56</script>57```5859**Correct - Options API:**60```js61export default {62emits: ['submit', 'cancel', 'update'],6364methods: {65handleSubmit() {66this.$emit('submit', this.formData)67}68}69}70```7172## TypeScript Typed Emits7374**Correct - Type-based declaration (recommended for TypeScript):**75```vue76<script setup lang="ts">77interface User {78id: number79name: string80}8182const emit = defineEmits<{83submit: [data: FormData]84cancel: []85'update:modelValue': [value: string]86select: [user: User, index: number]87}>()8889// Now TypeScript enforces correct payloads90emit('submit', formData) // OK91emit('submit') // Error: Expected 1 argument92emit('select', user) // Error: Expected 2 arguments93emit('unknown') // Error: Unknown event94</script>95```9697**Alternative syntax (Vue 3.3+):**98```vue99<script setup lang="ts">100const emit = defineEmits<{101(e: 'submit', data: FormData): void102(e: 'cancel'): void103(e: 'update:modelValue', value: string): void104}>()105</script>106```107108## Event Validation109110You can validate event payloads at runtime:111112**Correct - Validation functions:**113```vue114<script setup>115const emit = defineEmits({116// No validation, just declaration117cancel: null,118119// Validate payload120submit: (payload) => {121if (!payload.email) {122console.warn('Submit event requires email')123return false124}125return true126},127128// Validate with type checking129click: (id) => typeof id === 'number'130})131</script>132```133134Returning `false` from a validator logs a console warning but doesn't prevent the event from being emitted.135136## Benefits of Declaring Emits137138### 1. Fallthrough Attribute Separation139140Without declaration, native event listeners fall through to the root element:141142```vue143<!-- ParentComponent.vue -->144<ChildComponent @click="handleClick" />145```146147```vue148<!-- ChildComponent.vue - WITHOUT emits declaration -->149<template>150<!-- Native click listener falls through to button -->151<button>Click me</button>152</template>153```154155With declaration, Vue knows it's a component event:156157```vue158<script setup>159// Now Vue knows 'click' is a component event, not native160const emit = defineEmits(['click'])161</script>162```163164### 2. Self-Documentation165166```vue167<script setup>168// Clear contract: this component emits these events169const emit = defineEmits<{170'row-click': [row: TableRow]171'row-select': [row: TableRow, selected: boolean]172'page-change': [page: number]173'sort-change': [column: string, direction: 'asc' | 'desc']174}>()175</script>176```177178### 3. IDE Support179180With declarations, your IDE can:181- Autocomplete event names when using the component182- Show event payload types183- Warn about typos in event names184- Navigate to event definitions185186## $emit in Template vs emit in Script187188```vue189<script setup>190// $emit is available in template, but...191// emit() is needed in <script setup>192const emit = defineEmits(['submit'])193194function handleSubmit() {195// $emit doesn't work here - use emit()196emit('submit', data)197}198</script>199200<template>201<!-- $emit works in template -->202<button @click="$emit('submit', data)">Submit</button>203204<!-- Or use the declared emit function -->205<button @click="emit('submit', data)">Submit</button>206</template>207```208209## Reference210- [Vue.js Component Events - Declaring Emitted Events](https://vuejs.org/guide/components/events.html#declaring-emitted-events)211- [Vue.js Component Events - Events Validation](https://vuejs.org/guide/components/events.html#events-validation)212- [Vue 3 Migration - emits Option](https://v3-migration.vuejs.org/breaking-changes/emits-option)213