As we are diving deeper into Lightning Web Components (LWC) and we are getting familiar with the component-based structure in LWC, which is the ground work for building scalable applications in Salesforce. We understand that interfacing components require to communicate among them. But one thing that can be tricky even for seasoned developers, is mastering communication between components.
Today, we will go through the concepts to master Child-to-Parent communication in Lightning Web Components (LWC), which is a common scenario when working with hierarchical components. Let’s break it down step-by-step so you can grasp more easily and confident handling this in your future projects.
Table of Contents
How to implement child-to-parent communication in Lightning Web Components
In LWC, components follow a unidirectional data flow, meaning data typically moves from a parent component to its child through properties/attributes. However, there are plenty of situations where a child component needs to send data back up from the child-to-parent component.
For instance:
- A child form component might need to notify the parent when the user submits the form.
- A child modal component could send a “close” signal to its parent when the form is submitted/rejected.
- A child item in a list might pass some selected data back to the parent.
The ability to effectively communicate from child to parent is pivotal to building interactive and dynamic user interfaces.
Step-by-step guide to child-to-parent communication in Lightning Web Components
In LWC, the events are the primary mechanism for child-to-parent communication. The child component “dispatches” an event, and the parent “listens” for that event to handle it. There are different standard events we have used already in previous lessons like onclick, onkeyup, etc. Here we will be learning about the custom events.
Let’s get practical with an example!
1. Dispatching an Event
Let’s say we have a child component that needs to send some data back to the parent component on click on a button.
Here’s the child component’s HTML:
Example childComponent.html
<template>
<lightning-button
label="Send to Parent"
onclick={handleClick}>
</lightning-button>
</template>
And here in the JavaScript for the child component, we create and dispatch a custom event with the message attached on the detail. We can choose to send or not send any details while dispatching the event.
Example childComponent.js
import { LightningElement } from 'lwc';
export default class ChildComponent extends LightningElement {
handleClick() {
// Step 1: Create a new custom event
const customEvent = new CustomEvent('sendtoparent', {
detail: { message: 'Hello from Child!' }
});
// Step 2: Dispatch the custom event
this.dispatchEvent(customEvent);
}
}
Explaination :
- Dispatching the Event: We use the
this.dispatchEvent(customEvent)
to dispatch the event, which can then be listened to, by the parent component. - CustomEvent Creation: In the
handleClick
method, we create a new custom event usingCustomEvent
. The event name is'sendtoparent'
(you can name this anything you like, but keep it meaningful). Thedetail
object contains the data we want to send from child to parent component. Here, we are sending a simple message'Hello from Child!'
.
Best Practices for naming an event in LWC
We must choose an only string name, with no uppercase and no spaces. We can use underscores but must keep it smaller but contextual.
2. Listening for the Event in the Parent Component
Now, on the parent component (parentComponent
), where we will add an event listener to listen to the event and handle the data sent by the child.
How to handle events in Lightning Web Components?
We add the event listener (on+eventname) onsendtoparent={handleEventFromChild}
on the parent component’s HTML:
Example parentComponent.html
<template>
<p>Message from Child: {messageFromChild}</p>
<c-child-component onsendtoparent={handleEventFromChild}></c-child-component>
</template>
And then on the JavaScript for the parent component we handle the event and retrieve any details if there:
Example parentComponent.js
import { LightningElement } from 'lwc';
export default class ParentComponent extends LightningElement {
messageFromChild;
handleEventFromChild(event) {
// Step 3: Capture the event detail
this.messageFromChild = event.detail.message;
}
}
Explaination :
- Listening to the Event: In the parent’s HTML template, we use the
<c-child-component>
tag to include the child component. Here, we add the event listeneronsendtoparent={handleEventFromChild}
. Thesendtoparent
matches the event name we dispatched from the child. - Handling the Event: In the parent’s JavaScript, the
handleEventFromChild
method is triggered when the event is caught. Theevent.detail.message
contains the data passed from the child, which we then store in themessageFromChild
property and display in the template.
3. Key Takeaway Points to Remember
- CustomEvent: Use the
CustomEvent
constructor to create your event and pass data via thedetail
property. Keep your event names clear and descriptive.
new CustomEvent('showprompt',{details:null})
There are other two parameters apart from the details here, which are default false as below. We will learn more about bubbles
, and composed
on the next sub topic in this chapter itself.
new CustomEvent('showprompt',{bubbles:false, composed:false, details:null})
- dispatchEvent: Always use
this.dispatchEvent()
to dispatch the event from the child component. We can also send and dispatch as below:
this.dispatchEvent(new CustomEvent('showprompt',{details:null}))
- Event Listener in Parent: In the parent component’s template, use
on<eventname>
to listen for the custom event, and define a handler function to process the event data.
<template if:true={showTextPrompt}>
<p>This is a text Prompt</p>
</template>
<c-child-component onshowprompt={enablePrompt}>
showTextPrompt = false
enablePrompt(event) {
// set a template to true or something on the parent
this.showTextPrompt = true;
}
- Another noteworthy point here is to understand the
detail
. Thedetail
can have a single string data passed to it as:
const customEvent = new CustomEvent('sendtoparent', {
detail: 'Hello from Child!'
});
// And will be captured in the parent component later as :this.messageFromChild = event.detail;
Or the detail
can pass an object form of data as in earlier example like
const customEvent = new CustomEvent('sendtoparent', { detail: { message: 'Hello from Child!' } });
// And later be captured and treated like below:this.messageFromChild = event.detail.message;
4. Why Events Over Direct Data Binding?
Child components should remain decoupled from their parents, meaning they should only communicate via events, rather than directly accessing or modifying the parent’s state. This keeps the code more maintainable and reusable.
By using events, the child component doesn’t care what the parent does with the data—it simply dispatches the event and continues with its own logic.
5. Event Propagation in LWC
After an event is fired by a component, it propagates up through the DOM of that component. To understand where events can be handled, we should also learn about how they propagate in the first place.
Events bubble up through the DOM structure; while the properties/attributes go down; that’s how the children and parents communicate in LWC. When we create an event in LWC, we can define the event propagation behavior based on two properties on the event, bubbles
and composed
.
Bubbling Phase Properties
Event.bubbles
is a boolean value indicating whether the event can bubble up through the DOM or not. The default value is set tofalse
.
In some cases, we want an event to bubble up to the multiple levels in the component hierarchy. This can be achieved by setting the bubbles
property of the event to true
. What it essentially means is that we can now communicate upwards in the hierarchy as well.
Example
const customEvent = new CustomEvent('sendtoparent', {
detail: { message: 'Hello from Child!' },
bubbles: true
});
When an event bubbles, it can be handled not only by the direct parent but by any component up the hierarchy that is listening for the event. This is useful in more complex component structures interfacing each other.
Event.composed
is also a boolean value indicating whether the event can pass through the shadow boundary. The default value is set tofalse
.
Each component’s internal DOM is encapsulated in a shadow DOM. The shadow boundary is the line between the regular DOM (also called the light DOM) and the shadow DOM. Look at the amazing structure below to understand it better.
So when we say bubbles
is set to false, the event doesn’t bubble up through the DOM. And when we say composed
is set to false, doesn’t cross the shadow boundary (apart from its own shadow boundary). The only way to listen to this event is to add an event listener directly on the component that dispatches the event, i.e. the event bubbles up to c-child
only.
<c-child oneventname={method}></c-child>
We can read more about the event configuration in LWC here.
Final Thoughts
Congratulations! We have just mastered the Child-to-Parent communication in Lightning Web Components (LWC) using custom events for building dynamic Salesforce applications with a clean, maintainable component structure. Lets experiment more of these with more complex use cases. If you haven’t checked parent to child communication in LWC yet, well you should definitely read this lesson!
We also checkout other modes of communication in upcoming lessons like sibling to sibling communication in LWC, as well as Publish Subscribe model in LWC.
In the meantime if you have any questions or run into any issues, don’t hesitate to reach out! Shoot your queries below or resolve any for me. You can also checkout the forum at the home 🙌