Skip to main content

SF-9009 · Compare · Medium

What is Lightning Message Service and how does it differ from pub-sub?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

When two components on the same Lightning page need to talk but don’t share a parent-child relationship — say a list on the left and a detail panel on the right — CustomEvent won’t reach across. That’s the job of Lightning Message Service (LMS).

What LMS is

LMS is a Salesforce-provided messaging API that lets components communicate across the DOM regardless of where they sit in the page hierarchy or which framework they’re built in. The three pieces:

  1. A Message Channel — a .messageChannel-meta.xml metadata file declaring the channel and its fields.
  2. The lightning/messageService module — JS APIs for publishing and subscribing.
  3. The @wire(MessageContext) adapter — gives the component its identity in the LMS system.

Setting up a channel

<!-- messageChannels/RecordSelected.messageChannel-meta.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>Record Selected</masterLabel>
    <isExposed>true</isExposed>
    <lightningMessageFields>
        <fieldName>recordId</fieldName>
    </lightningMessageFields>
</LightningMessageChannel>

Publishing a message

import { LightningElement, wire } from 'lwc';
import { publish, MessageContext } from 'lightning/messageService';
import RECORD_SELECTED from '@salesforce/messageChannel/RecordSelected__c';

export default class ContactList extends LightningElement {
    @wire(MessageContext) messageContext;

    handleSelect(event) {
        publish(this.messageContext, RECORD_SELECTED, {
            recordId: event.detail.contactId
        });
    }
}

Subscribing to a message

import { LightningElement, wire } from 'lwc';
import { subscribe, unsubscribe, MessageContext } from 'lightning/messageService';
import RECORD_SELECTED from '@salesforce/messageChannel/RecordSelected__c';

export default class ContactDetail extends LightningElement {
    @wire(MessageContext) messageContext;
    subscription = null;
    selectedId;

    connectedCallback() {
        this.subscription = subscribe(this.messageContext, RECORD_SELECTED,
            (msg) => { this.selectedId = msg.recordId; });
    }

    disconnectedCallback() {
        unsubscribe(this.subscription);
        this.subscription = null;
    }
}

The disconnectedCallback cleanup is not optional. Without it, the subscription survives the component and leaks memory across navigation.

LMS vs the old pub-sub library

Before LMS, the community used a pubsub.js helper from a Salesforce sample app. It worked, but it had three problems:

pub-sub (legacy)LMS (official)
Officially supported by SalesforceNoYes
Works across LWC, Aura, and VisualforceLWC onlyAll three
Survives navigation in console appsNo — singleton scoped to pageYes
Subscriber registryStatic module — fragilePer-context, garbage-collected
Strongly typed channelsNoYes — schema declared in metadata
In-built scope controlNoYes — APPLICATION vs ACTIVE scope

If you see pubsub.js in a codebase today, treat it as tech debt. Anything new should use LMS.

LMS scopes

subscribe() accepts a third argument that controls how aggressively the listener fires:

subscribe(this.messageContext, CHANNEL, handler, { scope: APPLICATION_SCOPE });
  • APPLICATION_SCOPE — receive messages from anywhere in the app, including inactive console tabs.
  • Default scope — receive only from the same active page/tab.

In a console app, APPLICATION_SCOPE lets a utility-bar component pick up a message from any open subtab. Without it, the message is scoped to the tab it was published from.

When to use which messaging tool

ScenarioTool
Child to direct parentCustomEvent
Sibling on the same page, different DOM branchesLMS
Parent to child@api properties
Cross-framework (LWC ↔ Aura ↔ Visualforce)LMS
Page-wide global stateLMS with APPLICATION_SCOPE
Server-pushed events from ApexPlatform Events + lightning/empApi (Streaming API)

Don’t use LMS as a shortcut to avoid passing props down a small tree — events that could be CustomEvents are easier to debug than channel messages.

Interview signal

The interviewer wants to hear that you understand LMS replaced pub-sub because pub-sub couldn’t cross frameworks or survive console navigation. If you can explain MessageContext, the schema-driven channel, and the cleanup pattern in disconnectedCallback, you’ve covered everything that matters.

Verified against: LWC Developer Guide — Communicate Across the DOM with Lightning Message Service. Last reviewed 2026-05-17 for Spring ‘26 release.