--- title: Component Events Don't Bubble impact: MEDIUM impactDescription: Vue component events only reach direct parent - sibling and grandparent communication requires alternative patterns type: gotcha tags: [vue3, events, emit, event-bubbling, provide-inject, state-management] --- # Component Events Don't Bubble **Impact: MEDIUM** - Unlike native DOM events, Vue component events do NOT bubble up the component tree. When a child emits an event, only its direct parent can listen for it. Grandparent components and siblings never receive the event. This is a common source of confusion for developers coming from vanilla JavaScript where events naturally bubble up the DOM. ## Task Checklist - [ ] Only expect events from direct child components - [ ] Use provide/inject for deeply nested communication - [ ] Use state management (Pinia) for complex cross-component communication - [ ] Re-emit events at each level if manual bubbling is needed - [ ] Consider whether your component hierarchy is too deep ## The Problem ```vue ``` ```vue ``` ```vue ``` ## Solution 1: Re-emit at Each Level (Simple Cases) Manually forward events through each component. **Correct:** ```vue ``` ```vue ``` ```vue ``` **Drawback:** Becomes tedious with deeply nested components. ## Solution 2: Provide/Inject (Ancestor Communication) For deeply nested components, provide a callback from the ancestor. **Correct:** ```vue ``` ```vue ``` ```vue ``` **Advantages:** - Skips intermediate components - No prop drilling or re-emitting - Works at any nesting depth ## Solution 3: State Management (Complex Applications) For cross-component communication, especially between siblings or unrelated components, use Pinia. **Correct:** ```js // stores/selection.js import { defineStore } from 'pinia' import { ref } from 'vue' export const useSelectionStore = defineStore('selection', () => { const selectedItem = ref(null) function selectItem(item) { selectedItem.value = item } return { selectedItem, selectItem } }) ``` ```vue ``` ```vue ``` ## Solution 4: Event Bus (Use Sparingly) For truly decoupled components, a simple event bus can work: ```js // eventBus.js import mitt from 'mitt' export const emitter = mitt() ``` ```vue ``` ```vue ``` **Warning:** Event buses make data flow hard to trace. Prefer provide/inject or state management. ## Comparison Table | Method | Best For | Complexity | |--------|----------|------------| | Re-emit | 1-2 levels deep | Low | | Provide/Inject | Deep nesting, ancestor communication | Medium | | Pinia/State | Complex apps, sibling communication | Medium | | Event Bus | Truly decoupled, rare cases | Low (but risky) | ## Native Events DO Bubble Note that native DOM events attached to elements still bubble normally: ```vue ``` Only Vue component events (those emitted with `emit()`) don't bubble. ## Reference - [Vue.js Component Events](https://vuejs.org/guide/components/events.html) - [Vue.js Provide/Inject](https://vuejs.org/guide/components/provide-inject.html)