You can use pubsub
library or lightning message service to communicate between components that are not in same DOM hierarchy.
These methods can be used to communicate between sibling components in an outer lwc component or between to lwc components that are dropped separately in lightning app builder.
Here we will go through an example of lwcComponentA that displays three car company names using radio button.
When a car company is selected in this component we will fire a carselected
event from lwcComponentA with the name selected by the user.
A separate lwcComponentB
, that is outside direct DOM hierarchy of lwcComponentA, will listen to this event and display the selected name in lwcComponentB
.
Example
1) Create a utility lwc component with name pubsub
.
Copy paste below code into it's JavaScript file.
Please note that this is a utility component provided by Salesforce. But current version available in the link
is doing pageRef check and it is not working as expected. So use below version shared by another Salesforce team member instead.
/** | |
* A basic pub-sub mechanism for sibling component communication | |
* | |
* TODO - adopt standard flexipage sibling communication mechanism when it's available. | |
*/ | |
const events = {}; | |
/** | |
* Registers a callback for an event | |
* @param {string} eventName - Name of the event to listen for. | |
* @param {function} callback - Function to invoke when said event is fired. | |
* @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound. | |
*/ | |
const registerListener = (eventName, callback, thisArg) => { | |
if (!events[eventName]) { | |
events[eventName] = []; | |
} | |
const duplicate = events[eventName].find(listener => { | |
return listener.callback === callback && listener.thisArg === thisArg; | |
}); | |
if (!duplicate) { | |
events[eventName].push({ callback, thisArg }); | |
} | |
}; | |
/** | |
* Unregisters a callback for an event | |
* @param {string} eventName - Name of the event to unregister from. | |
* @param {function} callback - Function to unregister. | |
* @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound. | |
*/ | |
const unregisterListener = (eventName, callback, thisArg) => { | |
if (events[eventName]) { | |
events[eventName] = events[eventName].filter( | |
listener => | |
listener.callback !== callback || listener.thisArg !== thisArg | |
); | |
} | |
}; | |
/** | |
* Unregisters all event listeners bound to an object. | |
* @param {object} thisArg - All the callbacks bound to this object will be removed. | |
*/ | |
const unregisterAllListeners = thisArg => { | |
Object.keys(events).forEach(eventName => { | |
events[eventName] = events[eventName].filter( | |
listener => listener.thisArg !== thisArg | |
); | |
}); | |
}; | |
/** | |
* Fires an event to listeners. | |
* @param {object} pageRef - Reference of the page that represents the event scope. | |
* @param {string} eventName - Name of the event to fire. | |
* @param {*} payload - Payload of the event to fire. | |
*/ | |
const fireEvent = (pageRef, eventName, payload) => { | |
if (events[eventName]) { | |
const listeners = events[eventName]; | |
listeners.forEach(listener => { | |
try { | |
listener.callback.call(listener.thisArg, payload); | |
} catch (error) { | |
// fail silently | |
} | |
}); | |
} | |
}; | |
export { | |
registerListener, | |
unregisterListener, | |
unregisterAllListeners, | |
fireEvent | |
}; |
2) Create a lwc component with name lwcComponentA
.green-border { | |
border: 3px solid green; | |
padding: 50px; | |
width: 250px; | |
height: 250px; | |
float:left; | |
} |
<template> | |
<div class="green-border"> | |
<h1>Component A (Publisher)</h1> | |
<br/> | |
<lightning-radio-group | |
name="cars" label="Cars" options={cars} value={selectedValue} type="radio" onchange={sendDataToComponentB}> | |
</lightning-radio-group> | |
</div> | |
</template> |
import { LightningElement, wire } from 'lwc'; | |
import { fireEvent } from 'c/pubsub'; | |
import { CurrentPageReference } from 'lightning/navigation'; | |
export default class LwcComponentA extends LightningElement { | |
@wire(CurrentPageReference) pageRef; | |
cars = [ | |
{ label: "Audi", value: "Audi" }, | |
{ label: "Tesla", value: "Tesla" }, | |
{ label: "BMW", value: "BMW" }, | |
]; | |
selectedValue = ''; | |
sendDataToComponentB(event) { | |
fireEvent(this.pageRef ,'carselected', event.target.value); | |
} | |
} |
3) Create a lwc component with name lwcComponentB
.red-border { | |
border: 3px solid red; | |
padding: 50px; | |
width: 250px; | |
height: 250px; | |
float:left; | |
margin-left: 10px; | |
} |
<template> | |
<div class="red-border"> | |
<h1>Component B (Subscriber)</h1> | |
<br/> | |
Value obtained from component A | |
<br/> | |
<b>{valueGotFromA}</b> | |
</div> | |
</template> |
import { LightningElement } from 'lwc'; | |
import { registerListener, unregisterAllListeners } from 'c/pubsub'; | |
export default class LwcComponentB extends LightningElement { | |
valueGotFromA = ''; | |
connectedCallback(){ | |
registerListener('carselected', this.diplaySelectedCar, this); | |
} | |
disconnectedCallback() { | |
unregisterAllListeners(this); | |
} | |
diplaySelectedCar(carName) { | |
this.valueGotFromA = carName; | |
} | |
} |
3) Create a parent lwc component with name parentLwc
that can contain both components
<template> | |
<div class="parent"> | |
<c-lwc-component-a></c-lwc-component-a> | |
<c-lwc-component-b></c-lwc-component-b> | |
</div> | |
</template> |
3) Now if you surface the parent component in lightning experience, it will look like

Explanation
In lwcComponentA
component, when value of car is changed in the radio button,
it calls JavaScript method in controller sendDataTolwcComponentB
.
This function uses fireEvent
method in pubsub
library and fires a custom event with name carselected
.
Selected carname is also passed as data in the event payload.
lwcComponentB
component, has a listener added for carselected
event.
It is achieved by using registerListener
method in pubsub utility.
Inside connected callback we add a listener using the line registerListener('carselected', this.diplaySelectedCar, this);
.
This basically tells "whenever a carselected event is received, call diplaySelectedCar
method in JavaScript controller.
Finally when diplaySelectedCar
method is called,
we take value from the event payload and assigns to valueGotFromA
variable in JavaScript
Things to Note
- This is a common pattern to pass values between independent lwc components in same page.
-
Make sure that you are using the pubsub library shared above.
Otherwise you might get an error
pubsub listeners need a "@wire(CurrentPageReference) pageRef" property
.