Lightning Container Component: Building Components with React, Angular, and Other Libraries

The Lightning Container Component allows you to securely host components and applications built with third-party frameworks in the Salesforce Lightning Experience. In this blog post, I describe how to use the Lightning Container Component through a series of examples.

To avoid security exploits, an application composition framework must provide the right level of isolation between components. An untrusted component shouldn’t be able to gain access to parts of the DOM owned by other components. Without the right level of isolation, a vulnerability in one component can provide access to sensitive data in other components and jeopardize the security of the entire system. The Lightning Component Framework provides two isolation mechanisms: LockerService and the Lightning Container Component.

LockerService wraps standard DOM objects like window, document, and element to provide namespace-based access control. Check out this blog post for more information. Using LockerService, custom components are hosted in the same DOM as the main Salesforce app.

If your JavaScript application doesn’t work within the LockerService security constraints, you can host it in the Lightning Container Component. The Lightning Container Component is a convenience component that wraps your JavaScript application in an iFrame: the browser’s native isolation mechanism. The Lightning Container Component also comes with a library that you can use in your JavaScript application to make calls to Apex controller methods, and to communicate with the hosting component using a secure message channel.

Project setup

The Lightning Container Component library is available as a JavaScript module that you can install as a dependency in your JavaScript application using npm. Because JavaScript modules are not yet supported consistently across all browsers, applications that use modules (and other emerging JavaScript features) require a build process using a tool like webpack or rollup that transpiles not-yet-supported-features into ECMAScript 5.

NOTE: It is possible to use the Lightning Container Component library in plain old JavaScript (ECMAScript 5) without using modules and without a build process. Check out example 4 below to see how.

Here are the typical steps to use the Lightning Container Component library in your JavaScript application:

  1. Install the Lightning Container Component module (lightning-container) as a dependency:
    npm install lightning-container --save
  2. In your JavaScript code, import the lightning-container module as follows:
    import LCC from 'lightning-container';
    
  3. Use the Lightning Container Component API to communicate with the hosting component and to make Apex calls.
    Method Description
    LCC.sendMessage(message) Send a message to the hosting Lightning Component
    LCC.addMessageHandler(handler) Register a listener for messages sent by the hosting Lightning Component
    LCC.removeMessageHandler(handler) Remove a listener for messages sent by the hosting Lightning component
    LCC.callApex(method, params, callback, config) Call a method in an Apex controller
  4. Build your application using a build tool like webpack or rollup (see examples below).
  5. Upload your JavaScript application as a static resource.
  6. Create a Lightning component that wraps your JavaScript application using the lightning:container component.
    <aura:component>    
        <lightning:container src="{!$Resource.my_js_app + '/index.html'}" 
                onmessage="{!c.handleMessage}" 
                onerror="{!c.handleError}"/>
    </aura:component>
    

Examples

The examples below are available as a Salesforce DX project in this Github repository. The JavaScript applications are located in the js-apps folder. Each application comes with a webpack build configuration. When you build the application (npm run build), webpack automatically outputs the results (bundle.js) in the staticresources folder corresponding to the application.

Example 1: Communicating with the hosting component

This example demonstrates bi-directional communication between the Lightning Container Component and the JavaScript application it is hosting.

Check out the source code:

Code Highlights
Lightning Component: Sending a message to the JavaScript app

var message = {
    name: "General",
    value: component.get("Hello World!")
};
component.find("jsApp").message(message);

JavaScript App: Listening to messages from the parent Lightning Component

LCC.addMessageHandler(function(message) {
    console.log(message.value);
});

JavaScript App: Sending a message to the parent Lightning Component

var msg = {
    greeting: "Hello World!"
};
LCC.sendMessage(msg);

Lightning Component: Listening to messages from the JavaScript app

<lightning:container src="{!$Resource.lcc_sample_messaging + '/index.html'}"
           onmessage="{!c.handleMessage}"/>


handleMessage: function (component, event, helper) {
    var message = event.getParams();
    console.log(message.payload.greeting);
}

Example 2: Calling Apex methods

This example is a simple contact lookup built as a plain JavaScript application. It demonstrates how to call Apex methods from a JavaScript application hosted in a Lightning Container Component.

Check out the source code:

Code highlights
To make an Apex controller available, you have to declare it in a manifest.json file in the root folder of your JavaScript app:

// manifest.json
{
    "landing-pages" : [
        {
            "path": "index.html",
            "apex-controller": "ContactController"
        }
    ]
}

You can then invoke the Apex controller methods as follows:

// app.js
LCC.callApex("ContactController.getContacts",
                 searchKey,
                 resultHandler,
                 {escape: true});

When you click a contact, a message is sent to the hosting Lightning Component so it can take the appropriate action (in this case, open the selected contact’s record page):

function itemClicked(event) {
    var recordId = event.target.dataset.id;
    if (recordId) {
        var msg = {
            action: 'openDetails',
            recordId: recordId
        };
        LCC.sendMessage(msg);
    }
}

Example 3: Using React

This example is a React implementation of the contact lookup demonstrated in the previous example.

Check out the source code:

Example 4: Using ECMAScript 5

You can also use the Lightning Container Component library without using modules and without a build process. This example is a plain old JavaScript implementation of the contact lookup application demonstrated in the previous example.

Check out the source code:

Because there is no build process involved in this example, the source code is maintained directly in the staticresources folder.

Code highlights
The Lightning Container Component automatically injects the global LCC object in the iFramed application DOM. The lcc.js script included in index.html adds convenience methods to the LCC object to mirror the way the lightning-container module works.

Example 5: D3 visualization library

This example wraps a standard D3 sample to demonstrate how to leverage the D3 visualization library using the Lightning Container Component.

Check out the source code:

Summary

The Lightning Container Component provides a convenient approach to integrate JavaScript applications built with third-party libraries in the Lightning experience, especially when these applications don’t work within the LockerService security constraints.The Lightning Container Component library makes it easy to call Apex controller methods and to communicate with the hosting Lightning Component.

Resources

Leave your comments...

Lightning Container Component: Building Components with React, Angular, and Other Libraries