Skip to main content

How to Implement Lightning Message Service (LMS) in Salesforce LWC: Step-by-Step Guide

In plain words: Lightning Message Service (LMS) is Salesforce's official publish-subscribe communication framework that lets unrelated components—across Lightning Web Components (LWC), Aura components, and Visualforce pages on the same Lightning page—talk to each other seamlessly without parent-child hierarchies.

Before LMS, communicating between sibling components or across different UI frameworks required messy window-event hacks or custom pubsub modules. Lightning Message Service solves this by providing a standardized, secure messaging architecture defined by a metadata-backed Message Channel.

Prerequisites

  • A Salesforce Developer Edition org, Sandbox, or Scratch Org.
  • Salesforce CLI (sf) installed and authenticated.
  • Basic understanding of LWC lifecycle hooks (connectedCallback and disconnectedCallback) and wire adapters.

Step 1: Create the Lightning Message Channel Metadata

In Salesforce, LMS channels are defined as metadata XML files in your project directory rather than client-side JavaScript files. Create the channel folder and XML definition:

File Path: force-app/main/default/messageChannels/SampleMessageChannel.messageChannel-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>SampleMessageChannel</masterLabel>
    <isExposed>true</isExposed>
    <description>Message channel to broadcast data across unrelated components.</description>
    <lightningMessageFields>
        <fieldName>messageText</fieldName>
        <description>The message string passed across components.</description>
    </lightningMessageFields>
</LightningMessageChannel>
Warning Trap: A common mistake is attempting to create or release message channels directly via JavaScript APIs. Channels must always be deployed as LightningMessageChannel metadata with <isExposed>true</isExposed>. Import them using the scoped module prefix @salesforce/messageChannel/ChannelName__c.

Step 2: Build the Publisher Component

Create an LWC named lmsPublisher. The publisher imports the publish method and the message channel reference to broadcast payloads.

Markup (lmsPublisher.html):

<template>
    <lightning-card title="LMS Publisher" icon-name="utility:broadcast">
        <div class="slds-p-around_medium">
            <lightning-input 
                type="text" 
                label="Enter Message to Broadcast" 
                value={messageInput} 
                onchange={handleInputChange}>
            </lightning-input>
            <lightning-button 
                class="slds-m-top_small" 
                variant="brand" 
                label="Publish Message" 
                onclick={handlePublish}>
            </lightning-button>
        </div>
    </lightning-card>
</template>

Controller (lmsPublisher.js):

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

export default class LmsPublisher extends LightningElement {
    messageInput = '';

    @wire(MessageContext)
    messageContext;

    handleInputChange(event) {
        this.messageInput = event.target.value;
    }

    handlePublish() {
        const payload = {
            messageText: this.messageInput
        };
        publish(this.messageContext, SAMPLE_CHANNEL, payload);
    }
}

Step 3: Build the Subscriber Component

Create an LWC named lmsSubscriber. It uses subscribe inside connectedCallback() and cleans up subscriptions using unsubscribe inside disconnectedCallback().

Markup (lmsSubscriber.html):

<template>
    <lightning-card title="LMS Subscriber" icon-name="utility:inbox">
        <div class="slds-p-around_medium">
            <p class="slds-text-body_regular">
                Latest Received Message: <strong class="slds-text-color_success">{receivedMessage}</strong>
            </p>
        </div>
    </lightning-card>
</template>

Controller (lmsSubscriber.js):

import { LightningElement, wire } from 'lwc';
import { subscribe, unsubscribe, APPLICATION_SCOPE, MessageContext } from 'lightning/messageService';
import SAMPLE_CHANNEL from '@salesforce/messageChannel/SampleMessageChannel__c';

export default class LmsSubscriber extends LightningElement {
    receivedMessage = 'No messages received yet.';
    subscription = null;

    @wire(MessageContext)
    messageContext;

    connectedCallback() {
        this.subscribeToChannel();
    }

    subscribeToChannel() {
        if (!this.subscription) {
            this.subscription = subscribe(
                this.messageContext,
                SAMPLE_CHANNEL,
                (message) => this.handleMessage(message),
                { scope: APPLICATION_SCOPE }
            );
        }
    }

    handleMessage(message) {
        this.receivedMessage = message?.messageText || 'Empty payload received';
    }

    disconnectedCallback() {
        if (this.subscription) {
            unsubscribe(this.subscription);
            this.subscription = null;
        }
    }
}
360 Architecture Summary:
  • APPLICATION_SCOPE: Allows components to receive messages across separate browser tabs and non-active sub-tabs within Lightning Console apps.
  • Auto Teardown: Using @wire(MessageContext) automatically associates subscriptions with the component's lifecycle and clears them safely.
  • Cross-Tech Support: The same SampleMessageChannel__c can communicate simultaneously across LWC, Aura (lightning:messageChannel), and Visualforce (sforce.one.subscribe).

Step 4: Expose and Deploy Components

Update the metadata file (.js-meta.xml) for both components to make them available in Lightning App Builder:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>60.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>
Deployment Steps:
# Deploy message channels and components to your org
sf project deploy start

# Open your org
sf org open
  • In Salesforce, navigate to Setup > Lightning App Builder.
  • Place both lmsPublisher and lmsSubscriber anywhere on the same page.
  • Save, activate, and test real-time decoupled message passing.
Core Takeaway: Lightning Message Service provides a standard, declarative publish-subscribe event bus that eliminates tight coupling and simplifies cross-component communication across all Salesforce UI layers.