Building complex applications in Salesforce Lightning Web Components (LWC) requires multiple components to work together seamlessly. Because components operate in isolated DOM trees, child components cannot directly modify parent data. Instead, Salesforce uses an event-driven architecture powered by Custom Events to establish clean, loosely coupled communication channels.
1. How Component Communication Works in LWC
LWC follows strict directional data flow principles:
- Downward Communication (Parent to Child): Parents pass data down to child components using public properties decorated with
@api. - Upward Communication (Child to Parent): Child components communicate changes upward by creating and dispatching
CustomEventobjects. - Cross-Component Communication (Unrelated Components): Components that do not share a direct parent-child relationship communicate using the Lightning Message Service (LMS) or pub/sub event patterns.
- Event Creation: Instantiated via standard JavaScript
new CustomEvent('name', { detail })syntax. - Event Dispatch: Triggered using
this.dispatchEvent(event)in the child component controller. - Event Bubbling: Set
bubbles: trueandcomposed: trueif an event needs to travel across shadow DOM boundaries up to distant ancestors. - Handling Events: Caught in parent HTML templates using declarative inline handlers (e.g.,
onmyevent={handleEvent}).
2. Step-by-Step Example: Child-to-Parent Communication
Let's build a practical implementation where a child component (a button selector) notifies its parent component whenever a user selects a record.
The child component creates a custom event containing record details and dispatches it upward.
// childComponent.js
import { LightningElement } from 'lwc';
export default class ChildComponent extends LightningElement {
handleSelectRecord() {
const selectedEvent = new CustomEvent('recordselect', {
detail: { recordId: 'a0B8W00000GqXYZUAI', recordName: 'Acme Corp' },
bubbles: true,
composed: true
});
this.dispatchEvent(selectedEvent);
}
}
The parent component listens for the custom event in its HTML template and reads the passed payload in its JavaScript controller.
<!-- parentComponent.html -->
<template>
<lightning-card title="Parent Container" class="slds-m-around_medium">
<div class="slds-p-around_medium">
<p>Selected Record: <strong>{selectedName}</strong></p>
<!-- Listen for the custom event using on + event name in lowercase -->
<c-child-component onrecordselect={handleRecordSelection}></c-child-component>
</div>
</lightning-card>
</template>
// parentComponent.js
import { LightningElement, track } from 'lwc';
export default class ParentComponent extends LightningElement {
@track selectedName = 'None';
handleRecordSelection(event) {
// Extract data passed from child via event.detail
const recordData = event.detail;
this.selectedName = `${recordData.recordName} (${recordData.recordId})`;
}
}
3. Common Developer Traps & Best Practices
Custom event names in LWC are case-sensitive and must be referenced in lowercase within parent HTML templates. If you dispatch
new CustomEvent('recordSelect'), your parent HTML attribute must be named onrecordselect (all lowercase). Naming it onRecordSelect in the markup will cause the listener to fail silently.
@api properties for downward communication from parent to child, and dispatch CustomEvent with detail payloads for upward communication from child to parent.
- Encapsulate Data in
detail: Always pass custom parameters inside thedetailproperty object of theCustomEventconfiguration object. - Understand Bubbling Options: By default, LWC custom events do not bubble outside their immediate shadow DOM boundary. If ancestors higher up the component tree need to listen to the event, explicitly set
bubbles: trueandcomposed: true. - Use Lightning Message Service (LMS) for Distant Components: Do not rely on bubbling events through 5 layers of nested components; use the Lightning Message Service when communicating between completely unrelated components.
Summary
Custom Events are essential for building modular, decoupled Lightning Web Components. By understanding how to dispatch events with data payloads from child components and handle them declaratively in parent templates, developers can create clean, maintainable, and interactive Salesforce applications.