Lightning Message Service [Developer Preview]

In the Winter ’20 release, we are introducing a new feature that will benefit not only Visualforce developers, but LWC and Aura developers as well. We introduced it in our Developer Preview Live for Winter ’20 and wanted to bring in one of the developers behind this feature for a Q&A session.

What is Lightning Message Service?

Lightning Message Service is the first Salesforce technology designed to enable communication between Visualforce and Lightning Components anywhere on a Lightning Page.

LMS — as it’s affectionately known — provides a dead simple API to publish messages throughout Lightning Experience and subscribe to messages that originated from anywhere within Lightning Experience.

It is a front-end service that works in client-side user interfaces, which would include popped out utility bar item windows and parent/child iFrame windows (primarily Visualforce). All interactions have to originate from the same Lightning Experience application instance — i.e. same browser tab.

When should I use LMS?

If you’ve tried putting a Visualforce page in Lightning Experience and you dreaded figuring out how to work with raw iframes and postMessage to speak to Lightning Components, this service is for you. Our hope is that developers will see value in this handy service and integrate their existing Visualforce-based UIs into Lightning Experience with first-class communication.

Ask yourself, is there any time you want your Visualforce page to communicate with components on its containing Lightning Page? Those components can be Lightning Web Components, Aura Components or other Visualforce pages on the same Lightning Page. If so, you can’t go wrong with LMS.

Sounds a little like Aura Application Events but for all UI technologies?

Yes, similar to Aura Application Events, communication happens between components regardless of where they are in the DOM. However, Lightning Message Service takes that communication a step further. LMS is important to the grander Lightning ecosystem for two main reasons:

  1. As Lightning Web Components gain wider adoption, LMS positions itself as the standard publish-subscribe library in the UI, enabling components in any part of the DOM to communicate.
  2. LMS provides a standard means for Lightning Web Components to communicate with Aura Components. The fact that it speaks to Visualforce is a MAJOR plus.

Cool! What do I need to do to make it work?

Lightning Message Service is based on a new type of metadata: Lightning Message Channels. These are new lightweight, packageable components that you can create in your org and at runtime, publish and subscribe to messages on them.

By having this metadata type be packageable, we can associate a message channel to a specific namespace. Also, as the message channel developer, you can opt in to whether you want your message channel to be available to other namespaces. That means communication on LMS is Secure by Default. Components from other namespaces on the page won’t be able to snoop on your communication unless you allow them to.

Another benefit is at development time, if a Visualforce page or Lightning component tries to reference a message channel in a different namespace and that message channel is not “exposed,” that code won’t even compile. It will fail at save time. This has a huge benefit on your developer experience. You’ll immediately know if your code will work at runtime.

Additionally, we provide referential integrity between message channels and the code that references them. You can’t delete message channels that are referenced by other code, and we support auto-adding message channels to packages when their referencing code gets added to packages.

What does a Lightning Message Channel look like?

Here’s a sample definition for a SampleMessageChannel message channel that we use in our Metadata API Developer Guide.

<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>SampleMessageChannel</masterLabel>
    <isExposed>true</isExposed>
    <description>This is a sample Lightning Message Channel.</description>
</LightningMessageChannel>

The only required field is the masterLabel, which identifies the message channel in various UIs. The optional boolean isExposed field is false if you don’t specify it, and it tells our system whether or not the particular message channel is available to components in other namespaces.

So you save the message channel to your org first, and then use it in a Visualforce page or Lightning component?

Precisely! We recommend using the Salesforce CLI and a DX project, where you’ll place your message channel definition with the suffix .messageChannel-meta.xml in the force-app/main/default/messageChannels directory.

Deploy the message channel to your org using sfdx force:source:deploy (even better, sfdx force:source:push in a scratch org), ensuring that you’re using at least API v47. Then your org knows about the message channel, and you can reference it in code. See our Create a Message Channel article in our Developer Guide.

How do I reference message channels in another namespace?

Simply prefix the message channel name with the namespace followed by two underscores. So if SampleMessageChannel was from the example namespace, you’d say example__SampleMessageChannel__c. This part of the message channel reference syntax is consistent across UI technologies.

Speaking of code, can I check out the Lightning Message Service API?

Thought you’d never ask! Each technology has its own idiomatic way of referencing message channel metadata and interacting with the API.

Use LMS with Visualforce

We introduced new sforce.one APIs — publish, subscribe, and unsubscribe — in Visualforce to interact with LMS. They’re only available in Lightning.

The first parameter to publish and subscribe is the message channel reference, which you can get from a new $MessageChannel Global Variable inside of a {! } formula expression.

Here’s a sample Visualforce page that references the SampleMessageChannel Lightning Message Channel, adapted from our Visualforce Developer Guide. It uses publish, where the second parameter is the message payload. Presume the publishMC function below is bound to a button click in the rest of the HTML.

<apex:page>
    ...
    <script>
        // Load the Message Channel token in a variable
        var SAMPLEMC = "{!$MessageChannel.SampleMessageChannel__c}";
        function publishMC() {
            const message = {
                recordId: "some string",
                recordData: { value: "some value" } 
            }
            sforce.one.publish(SAMPLEMC, message);
        }
    </script>
    ...
</apex:page>

The call to subscribe is similar, but its second parameter takes a callback function that gets invoked when a message is published on that channel. subscribe will return a handle, which can be passed into unsubscribe if we no longer want to listen on that message channel.

Here’s a sample of how to subscribe and unsubscribe in Visualforce. Presume the subscribeMC and unsubscribeMC functions below are each bound to button clicks in the rest of the HTML and that there’s a textarea on the page with id="messageTextArea". Full example is in the Visualforce Developer Guide.

<apex:page>
    ...
    <script>
        // Load the Message Channel token in a variable
        var SAMPLEMC = "{!$MessageChannel.SampleMessageChannel__c}";
        var subscription;

        function handleMessage(message) {
            var textArea = document.querySelector("#messageTextArea");
            textArea.innerHTML = message ? JSON.stringify(message, null, '\t') : 'no message payload';
        }

        function subscribeMC() {
            if (!subscription) {
                subscription = sforce.one.subscribe(SAMPLEMC, handleMessage);
            }
        }

        function unsubscribeMC() {
            if (subscription) {
                sforce.one.unsubscribe(subscription);
                subscription = null;
            }
        }
    </script>
    ...
</apex:page>

The __c convention at the end of the message channel name in code denotes that it’s a custom message channel created on the Platform, but there is no other connection to custom objects.

Use LMS with Aura

Let’s now look at Aura, since it has some nice declarative aspects to the API.

To reference a message channel in your Aura component, add the lightning:messageChannel component to your Aura component. The type attribute takes the name of the message channel that the lightning:messageChannel component references. Here’s an example of how to publish, from the Lightning Aura Components Developer Guide.

<!-- myPublisherComponent.cmp -->
<aura:component>
    <lightning:button onclick="{!c.publishMC}">
    <lightning:messageChannel type="SampleMessageChannel__c"
                              aura:id="sampleMessageChannel"/>
</aura:component>
// myPublisherComponentController.js
({
    publishMC: function(cmp, event, helper) {
        var message = {
            recordId: "some string",
            recordData: { value: "some value" }
        };
        cmp.find("sampleMessageChannel").publish(message);
    }
})

To subscribe, declaratively use the onMessage attribute on lightning:messageChannel and specify a controller action.

<!-- mySubscriberComponent.cmp -->
<aura:component>
    <aura:attribute name="recordValue" type="String"/>
    <lightning:formattedText value="{!v.recordValue}" />
    <lightning:messageChannel type="SampleMessageChannel__c"
                              onMessage="{!c.handleMessage}"/>
</aura:component>
// mySubscriberComponentController.js
({
    handleMessage: function(cmp, message, helper) { 
        // Read the message argument to get the values in the message payload
        if (message != null && message.getParam("recordData") != null) {
            cmp.set("v.recordValue", message.getParam("recordData").value);
        }
    }
})

Use LMS with Lightning Web Components

To reference a message channel in LWC, import it from the new @salesforce/messageChannel scoped module. To use the LMS APIs, import the functions we’re interested in — publish, subscribe, unsubscribe, etc — from lightning/messageService.

Here’s an example using publish. You can assume that the publishMC function below is bound to a button click in the template.

// myPublisherComponent.js
import { LightningElement } from 'lwc';
import { createMessageContext, releaseMessageContext,
         publish } from 'lightning/messageService';
import SAMPLEMC from "@salesforce/messageChannel/SampleMessageChannel__c";

export default class MyPublisherComponent extends LightningElement {
    context = createMessageContext();
    
    publishMC() {
        const message = {
            recordId: "some string",
            recordData: { value: "some value" }
        };
        publish(this.context, SAMPLEMC, message);
    }
    
    disconnectedCallback() {
        releaseMessageContext(this.context);
    }
}

The createMessageContext and releaseMessageContext functions are unique to LWC. The context object provides contextual information about the Lightning Web Components using LMS. In disconnectedCallback, our recommendation is to call releaseMessageContext, which will unregister any subscriptions associated with the context.

Here’s an example using subscribe and unsubscribe. You can assume that the subscribeMC and unsubscribeMC functions below are each bound to a button click in the template and the receivedMessage property is bound to a textarea.

// mySubscriberComponent.js
import { LightningElement, track } from 'lwc';
import { createMessageContext, releaseMessageContext
         subscribe, unsubscribe } from 'lightning/messageService';

import SAMPLEMC from "@salesforce/messageChannel/SampleMessageChannel__c";

export default class MySubscriberComponent extends LightningElement {
    context = createMessageContext();
    subscription = null;
    @track receivedMessage = '';
  
    subscribeMC() {
       if (this.subscription) {
           return;
       }
       this.subscription = subscribe(this.context, SAMPLEMC, (message) => {
           this.handleMessage(message);
       });
    }
  
    unsubscribeMC() {
        unsubscribe(this.subscription);
        this.subscription = null;
    }

    handleMessage(message) {
        this.receivedMessage = message ? JSON.stringify(message, null, '\t') : 'no message payload';
    }

    disconnectedCallback() {
        releaseMessageContext(this.context);
    }
}

Wrapping it all up…

Here’s a little demo of LWC, Aura, and Visualforce all speaking to one another, with a cameo from components in pop out utility windows.

Lightning Message Service is in Developer Preview in Winter ’20, which means that it’s available in all Developer Edition and scratch orgs. Check out the Developer Guides, play around with it, and let us know what you think! Developer Preview is the perfect time to share feedback. Additionally, we have created many resources to help developers migrate to be more Lightning-centric, primarily by improving their Visualforce-based development experience and integration along the way. You can read this previous blog post to dive into those.

About the author

Trenton Johnson is a Software Engineer on the Lightning Bridge team working in Raleigh, NC. Yes, he really likes fried okra.

Leave your comments...

Lightning Message Service [Developer Preview]