Custom Events Not Reaching Parent Components
“CustomEvent doesn't bubble or compose by default. Your parent never heard you.”
What Happened
Had a grandchild component dispatching a 'save' event. The parent two levels up had an onsave handler. Nothing happened. I added console.logs in every component. The event was firing but dying at the first shadow DOM boundary. In React, events bubble by default. In LWC, they don't. I burned two hours on what was a one-line fix.
The Wrong Way
// grandchildComponent.js - event dies at shadow boundary
handleClick() {
this.dispatchEvent(new CustomEvent('save', {
detail: { recordId: this.recordId }
// bubbles defaults to false
// composed defaults to false
}));
}
<!-- parentComponent.html - never receives the event -->
<template>
<c-child-wrapper onsave={handleSave}>
<!-- grandchild is inside child-wrapper's template, event can't escape -->
</c-child-wrapper>
</template>The Right Way
// Option A: Bubble + compose through shadow DOM boundaries
// grandchildComponent.js
handleClick() {
this.dispatchEvent(new CustomEvent('save', {
detail: { recordId: this.recordId },
bubbles: true,
composed: true // crosses shadow DOM boundaries
}));
}
// Option B (preferred): Re-dispatch at each level
// childWrapper.js - catch and re-dispatch
handleGrandchildSave(event) {
this.dispatchEvent(new CustomEvent('save', {
detail: event.detail
}));
}
<!-- childWrapper.html -->
<template>
<c-grandchild onsave={handleGrandchildSave}></c-grandchild>
</template>The Lesson
LWC events don't bubble or compose by default. For cross-boundary events, either set bubbles + composed, or re-dispatch at each level for better encapsulation.
Enjoyed this? Get more like it.
Glen's Musings — AI, investing, and building things. Occasional. Free.
More LWC Mistakes
Mixing Wire and Imperative Apex Calls for the Same Data
Pick one: reactive wire or imperative call. Mixing them creates ghost data.
Read moreRookieCalling querySelector Before the Component Renders
The DOM doesn't exist in connectedCallback. Stop looking for it there.
Read morePainfulMutating Objects Without Triggering Reactivity
LWC tracks property assignment, not deep object mutations.
Read more