Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
DictionaryMMessage Channel
DevelopmentIntermediate

Message Channel

A Message Channel is a Salesforce metadata record that defines a typed publish-and-subscribe channel for Lightning Message Service (LMS).

§ 01

Definition

A Message Channel is a Salesforce metadata record that defines a typed publish-and-subscribe channel for Lightning Message Service (LMS). Components publish a message on the channel, and any component subscribed to that same channel receives it. The channel itself is the named contract that both sides agree on.

LMS lets components talk to each other across the DOM on a single Lightning page, even when they sit in different component trees and share no parent. It works across Lightning Web Components, Aura components, and Visualforce pages embedded in the same page. The Message Channel is the piece you declare and deploy. The publish and subscribe functions are the runtime API that moves messages through it.

§ 02

How Message Channels move data across a Lightning page

The problem LMS was built to solve

A Lightning App Builder page is a mix of components from different sources. Some are standard Salesforce components, some are custom, and some come from AppExchange packages. They often need to coordinate. Picking a row in a list should filter a chart somewhere else on the page. The trouble is those components usually have no shared parent in the DOM, so a normal component event has nothing to bubble up to. Before LMS, teams worked around this with a community pubsub module built on the window object, or with parent-child events that only fired when the components happened to be nested together. Both approaches were fragile. The window-based trick was not namespace-safe, so a component in one package could accidentally see traffic meant for another. Lightning Message Service replaced these patterns with a supported framework feature. The Message Channel gives publishers and subscribers a single named contract, and Salesforce keeps the plumbing secure and consistent across UI technologies. That is why LMS is the recommended way to communicate across the DOM on a Lightning page.

Defining the channel as metadata

A Message Channel is metadata, not code. In a Salesforce DX project you create it under force-app/main/default/messageChannels, and the file name follows the format channelName.messageChannel-meta.xml. The metadata type behind it is LightningMessageChannel. Inside the XML you set a masterLabel for the friendly name, an optional description, and an isExposed flag. You then list one or more lightningMessageFields, each with a fieldName and an optional description. Those fields document the shape of the payload the channel is meant to carry. A channel named ContactSelected, for example, might declare a recordId field so everyone knows the message is supposed to include a contact Id. Because the channel lives in source, it deploys like any other metadata and travels through your version control and CI pipeline. Defining the contract once in metadata is what keeps publishers and subscribers in agreement, instead of every component inventing its own ad hoc event names and payload shapes.

Publishing a message

To publish, a Lightning Web Component imports the publish function from lightning/messageService and imports the channel itself from the scoped module @salesforce/messageChannel. The channel import looks like ContactSelected__c, the API name of the channel you deployed. The publish function takes three arguments: a message context, the channel reference, and the message payload. The payload is a plain JavaScript object whose properties line up with the fields you declared on the channel. To get the message context, the component wires the MessageContext adapter, also from lightning/messageService, with @wire(MessageContext). A typical publisher decorates a property with @wire(MessageContext) messageContext, then calls publish(this.messageContext, CONTACT_CHANNEL, { recordId: someId }) when the user does something. The wired context matters for more than convenience. It ties the publish call to the component's place on the page, and it handles cleanup when the component is destroyed, so you are not left managing that lifecycle by hand.

Subscribing and cleaning up

A subscriber imports subscribe and unsubscribe from lightning/messageService, plus the same channel reference from @salesforce/messageChannel. It also wires MessageContext the same way the publisher does. The component calls subscribe with the message context, the channel, and a handler function that runs every time a message arrives. The subscribe call returns a subscription object. Hold on to that subscription. When the component is removed, pass it to unsubscribe so the handler stops firing. The cleanest place to do this is the disconnectedCallback lifecycle hook, paired with subscribing in connectedCallback. Skipping the unsubscribe step is a common source of trouble. Leftover subscriptions keep running and can pile up as the user moves around, which shows up later as sluggish pages that are awkward to diagnose. When you wire MessageContext rather than fetching context another way, LMS can tear down subscriptions automatically as the component goes away, which removes a lot of the manual bookkeeping and the bugs that come with it.

Scope: active area versus the whole app

Where a subscriber listens is controlled by scope. The default is the active area, which roughly means the part of the page the user is currently working in. With default scope you do not pass any scope option at all, and the component only hears messages relevant to its active context. This is the right choice most of the time, because it keeps unrelated parts of the application from reacting to each other. Sometimes you want to hear messages no matter where they originate, including from utility bar items or pop-out utilities that stay open across navigation. For that, import APPLICATION_SCOPE from lightning/messageService and pass it in the fourth subscriberOptions argument, as subscribe(context, channel, handler, { scope: APPLICATION_SCOPE }). One thing to know: scope can only be set on the subscriber, and only when you are using @wire(MessageContext). Reach for application scope when a component genuinely needs the wider net, such as a persistent utility that should always react. Otherwise the narrower default keeps behavior predictable.

Secure by default and the isExposed flag

LMS communication is secure by default. Components from a different namespace on the same page cannot listen in on your channel unless you decide to let them. The control for this is the isExposed flag in the channel metadata. It defaults to false, which keeps the channel private to its own namespace. Set isExposed to true only when you genuinely want components in other namespaces to publish to or subscribe to the channel. This matters most for managed packages on AppExchange. If you ship a channel that should let a customer's own components react to events from your package, you expose it on purpose. If a channel is internal wiring between your own components, leave it unexposed so nothing outside your namespace can touch it. Both 1GP and 2GP packaging models support Message Channels. Treating isExposed as a deliberate security decision, rather than flipping it on for convenience, keeps your cross-component traffic from leaking into code you do not control.

Where LMS works and where it does not

Lightning Message Service runs in Lightning Experience with both standard and console navigation, and across Aura and LWC components on a Lightning page. It works in the Salesforce mobile app for Aura and LWC, though not for Visualforce there. It is also supported for Lightning components in Aura and LWR-based Experience Builder sites. There are real boundaries. LMS does not support Salesforce Tabs plus Visualforce sites, and it does not support Visualforce pages inside Experience Builder sites. Messages are also constrained by the iframe boundary, which is why Visualforce participation depends on the embedding container, and why the sforce.one subscribe and unsubscribe methods exist for certain iframe cases. For containers LMS does not cover, the community pubsub module on GitHub is a fallback, but Salesforce notes it is not officially supported or actively maintained. Keep delivery in mind too. Subscribers run their handlers when messages arrive, so a publisher that fires very frequently can make heavy subscribers feel slow. Throttling a noisy publisher is a design habit worth keeping.

§ 03

How to create a Message Channel

You create a Message Channel as metadata in a Salesforce DX project, then deploy it so components can publish and subscribe. There is no point-and-click builder for the channel itself. You author the XML, save it in the right folder, and push it to the org.

  1. Create the messageChannels folder

    In your Salesforce DX project, make sure the path force-app/main/default/messageChannels exists. This is where every Message Channel definition lives alongside the rest of your source.

  2. Add the channel file

    Create a file named for your channel using the format channelName.messageChannel-meta.xml, for example ContactSelected.messageChannel-meta.xml. The file name before the extension becomes the channel's API name.

  3. Declare the channel contract

    In the XML, set the masterLabel, an optional description, the isExposed flag, and one or more lightningMessageFields. Give each field a fieldName so publishers and subscribers know what the payload should contain.

  4. Deploy and reference it

    Deploy the metadata to your org. In a component, import the channel from @salesforce/messageChannel using its API name with the __c suffix, then call publish or subscribe from lightning/messageService.

Mandatory fields
masterLabelrequired

The human-readable label for the channel, shown where channels are listed. Required for a valid channel definition.

isExposedrequired

Boolean that decides whether components in other namespaces may use the channel. Defaults to false, which keeps the channel private to its own namespace.

lightningMessageFieldsrequired

One or more field entries, each with a fieldName, that document the shape of the message payload the channel carries.

Gotchas
  • Reference the channel with its __c suffix in the import, for example @salesforce/messageChannel/ContactSelected__c, or the import fails to resolve.
  • Leave isExposed at false unless another namespace truly needs the channel. Exposing it lets outside packages publish to and subscribe on it.
  • Always unsubscribe in disconnectedCallback. Subscriptions that are never cleaned up keep running and degrade page performance over time.
  • Scope can only be set on the subscriber, and only with @wire(MessageContext). You cannot scope a publisher.
§

Trust & references

Official documentation

Straight from the source - Salesforce's reference material on Message Channel.

Was this entry helpful?
Help us write better definitions. Quick reactions or detailed edit suggestions.

About the Author

Dipojjal Chakrabarti is a B2C Solution Architect with 29 Salesforce certifications and over 13 years in the Salesforce ecosystem. He runs salesforcedictionary.com to help admins, developers, architects, and cert/interview candidates sharpen their fundamentals. More about Dipojjal.

§

Test your knowledge

Q1. What is a Message Channel?

Q2. What problem do Message Channels solve?

Q3. When should you use parent-child patterns instead?

§

Discussion

Loading…

Loading discussion…