Vue Custom Events: Child-to-Parent Communication
Think of `$emit` as a child component sending a flare up to its parent. It's how children report actions upwards, reversing the top-down flow of props. Use it to trigger parent state changes, like closing a modal. The footgun: events don't bubble.
WHY IT EXISTS: Vue's component architecture is based on a parent-to-child data flow using props. But what happens when a child needs to tell the parent something, like "the user clicked me"? Custom events solve this by providing a dedicated channel for child-to-parent communication, completing the component communication loop.
THE MENTAL MODEL: An event emission is like a child component raising its hand and shouting a specific phrase (the event name) that only its direct parent is listening for. The parent can choose to react to that phrase. The child can also hand the parent an object (the payload) when it raises its hand, passing data along with the signal.
HOW IT WORKS: A child component calls the built-in emit method, providing an event name and an optional payload. For example, this.emit('update-status', 'completed'). The parent component uses the v-on directive (shorthand @) to listen for that specific event name on the child's tag, like <ChildComponent @update-status="handleStatusUpdate">. The handleStatusUpdate method in the parent will then be called, receiving 'completed' as its first argument. Vue automatically transforms camelCase event names (updateStatus) into kebab-case listeners (@update-status) in templates.
WHEN TO USE IT: Use custom events whenever a child component needs to inform its parent about an internal action or state change that the parent needs to know about. This is common for custom form inputs, buttons that trigger parent actions, or components like modals that need to be closed by the parent. It keeps the child encapsulated and the parent in control of the application state.
WHEN NOT TO USE IT: Avoid using $emit for communication between sibling components or deeply nested components. Events only travel one level up to the direct parent; they do not bubble like native DOM events. For cross-component communication that isn't strictly parent-child, a global state management library like Pinia is the correct tool.
ONE CANONICAL EXAMPLE: A child component, CustomButton.vue, has a button. When clicked, it emits an event to signal this action and passes a value. The child code is <button @click="$emit('increaseBy', 5)">Increase by 5</button>. The parent listens with <CustomButton @increase-by="(amount) => total += amount" />. Clicking the button in the child tells the parent to execute its inline function, which increments the parent's total data property by 5. The child decides the value, but the parent owns the state.
Read the original → vuejs.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.