Skip to main content

Command Palette

Search for a command to run...

PubSub Model

Published
4 min readView as Markdown

🔄 Communicating Between Unrelated Components in LWC Using the PubSub Model

In Salesforce Lightning Web Components (LWC), direct communication between unrelated components—those that don't share a parent-child relationship—cannot be accomplished using standard property or event passing. To overcome this limitation, the Publish-Subscribe (PubSub) Model offers a flexible solution for decoupled communication, enabling a clean, event-driven architecture across the Lightning Component DOM.

🎯 What is the PubSub Model?

The PubSub Model in LWC creates a communication channel for sibling or unrelated components rendered on the same page. It allows one component (the publisher) to fire an event and another component (the subscriber) to listen for and handle that event—without a direct hierarchical or structural dependency.

⚠️ Important Note: PubSub communication works only among components on the same page (within the same Lightning App Page or Record Page).


🧭 Overview of PubSub Communication Flow

  1. A shared utility component named pubsub manages event listeners and dispatchers.

  2. The Publisher Component captures input and fires an event using fireEvent().

  3. The Subscriber Component registers for that event using registerListener() and updates its state based on the data received.


🧱 Step-by-Step Implementation


1. 📡 pubsub Utility Module (The Event Channel)

This module facilitates registering, firing, and unregistering custom events among unrelated LWC components.

Tip: Create a Lightning Web Component named pubsub and delete its HTML file, retaining only the .js and .js-meta.xml files.

📁 pubsub.js

/**
 * A reusable PubSub implementation for sibling/unrelated LWC components.
 */

const eventRegistry = {};

// Compares two page references to ensure same context
const isSamePage = (refA, refB) => {
    const a = refA.attributes;
    const b = refB.attributes;
    return Object.keys({ ...a, ...b }).every((key) => a[key] === b[key]);
};

const registerListener = (eventName, callback, context) => {
    if (!context.pageRef) {
        throw new Error('PubSub requires a @wire(CurrentPageReference) pageRef');
    }

    if (!eventRegistry[eventName]) {
        eventRegistry[eventName] = [];
    }

    const alreadyRegistered = eventRegistry[eventName].some(
        listener => listener.callback === callback && listener.context === context
    );

    if (!alreadyRegistered) {
        eventRegistry[eventName].push({ callback, context });
    }
};

const unregisterListener = (eventName, callback, context) => {
    if (eventRegistry[eventName]) {
        eventRegistry[eventName] = eventRegistry[eventName].filter(
            listener => listener.callback !== callback || listener.context !== context
        );
    }
};

const unregisterAllListeners = (context) => {
    Object.keys(eventRegistry).forEach(eventName => {
        eventRegistry[eventName] = eventRegistry[eventName].filter(
            listener => listener.context !== context
        );
    });
};

const fireEvent = (pageRef, eventName, data) => {
    const listeners = eventRegistry[eventName];
    if (listeners) {
        listeners.forEach(listener => {
            if (isSamePage(pageRef, listener.context.pageRef)) {
                try {
                    listener.callback.call(listener.context, data);
                } catch (error) {
                    console.error(`Error delivering pubsub event: ${error}`);
                }
            }
        });
    }
};

export {
    registerListener,
    unregisterListener,
    unregisterAllListeners,
    fireEvent
};

2. 🧾 Publisher Component: eventBroadcaster

This component accepts user input and broadcasts it to other components using fireEvent().

🔧 eventBroadcaster.html

<template>
    <lightning-card title="Publisher Component">
        <div class="slds-p-around_medium">
            <lightning-input 
                type="text" 
                label="Enter Text to Share" 
                onchange={captureInput}>
            </lightning-input>
            <lightning-button 
                variant="brand" 
                label="Publish Message" 
                onclick={dispatchEventToSubscribers}>
            </lightning-button>
        </div>
    </lightning-card>
</template>

⚙️ eventBroadcaster.js

import { LightningElement, wire } from 'lwc';
import { fireEvent } from 'c/pubsub';
import { CurrentPageReference } from 'lightning/navigation';

export default class EventBroadcaster extends LightningElement {
    userInput;

    @wire(CurrentPageReference) pageRef;

    captureInput(event) {
        this.userInput = event.target.value;
    }

    dispatchEventToSubscribers() {
        fireEvent(this.pageRef, 'dataTransferEvent', this.userInput);
    }
}

3. 📬 Subscriber Component: eventReceiver

This component listens for the custom event and updates its display when new data is received.

🔧 eventReceiver.html

<template>
    <lightning-card title="Subscriber Component">
        <div class="slds-p-around_medium">
            <p><strong>Received Data:</strong> {sharedData}</p>
        </div>
    </lightning-card>
</template>

⚙️ eventReceiver.js

import { LightningElement, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';
import { registerListener, unregisterAllListeners } from 'c/pubsub';

export default class EventReceiver extends LightningElement {
    sharedData;

    @wire(CurrentPageReference) pageRef;

    connectedCallback() {
        registerListener('dataTransferEvent', this.updateData, this);
    }

    updateData(payload) {
        this.sharedData = payload;
    }

    disconnectedCallback() {
        unregisterAllListeners(this);
    }
}

🔍 Key Concepts Recap

ConceptDescription
fireEvent()Method in Publisher to broadcast data.
registerListener()Method in Subscriber to listen for event.
pageRefEnsures components are on the same page.
pubsub.jsActs as a central messaging utility.

✅ Benefits of PubSub in LWC

  • Loose coupling between components.

  • Reusability across different contexts.

  • Seamless data flow in app-level designs.

  • Avoids unnecessary re-renders or controller-level logic.


📌 Final Notes

  • Always ensure both components share the same pageRef, as this is crucial for the event to be received.

  • Unregister listeners in disconnectedCallback to avoid memory leaks or stale event handling.

  • PubSub is ideal for communication between sibling or unrelated components on the same Lightning page, but not across different pages or apps.


💡 Use Case Ideas

  • A global notification bell triggering updates in multiple widgets.

  • A search filter input updating multiple result panes.

  • Real-time updates across dashboard widgets.