Handle Lightning Out 2.0 App Events

Configure custom events so that embedded components and the host page can communicate with each other.

Overview 

Lightning Out 2.0 app components load in iframes instead of directly on the host page. To overcome communication restrictions across iframe boundaries, Lightning Out 2.0 wraps custom events in messages using window.postMessage(). This internal implementation allows event listeners on one side of the iframe boundary to detect events dispatched from the other side. From your perspective, events are still created and handled with the standard EventTarget and CustomEvent interfaces.

Recall that a Lightning Out 2.0 app includes Lightning Out 2.0 web components that mirror your embedded LWC components but run in the context of the host page. If you need a refresher, review Understand Lightning Out 2.0 Architecture. This mirroring extends to events.

If you add an event listener to a Lightning Out 2.0 web component, Lightning Out 2.0 also adds the event listener to the corresponding LWC component running in the Salesforce context.

1const loComponent = document.querySelector("c-lwc-component");
2loComponent.addEventListener("my-custom-event", (event) => {
3  console.log("Received event.detail: ${event.detail}");
4});

If you dispatch an event from a Lightning Out 2.0 web component, Lightning Out 2.0 also dispatches the event from the corresponding LWC component. When you create a custom event, set the bubbles and composed properties to true so that events can pass through the shadow DOM boundary.

1const loComponent = document.querySelector('c-lwc-component');
2loComponent.dispatchEvent(
3    New CustomEvent("my-custom-event", {
4        detail: "Hello!",
5        bubbles: true,
6        composed: true,
7    })
8);

Therefore, if an event is fired inside the Salesforce context, the event can bubble up from the LWC component event listener to the Lightning Out 2.0 web component event listener. Then, the event is handled in the host page context. And similarly, if an event is fired in the host page context, the event can propagate down from the Lightning Out 2.0 web component event listener to the LWC component event listener. Then, the event is handled in the Salesforce context.

Eventing Example 

Let’s demonstrate Lightning Out 2.0 event handling with some example code.

cardComponent.html 

Here’s a custom Lightning web component called c-card-component, which is a wrapper for the lightning-card component. The card has a lightning-button component that sends a message to the host page when clicked. The card also has a conditional directive that renders a new message card when a user clicks a button on the host page.

1<!--cardComponent.html-->
2<template>
3  <lightning-card title="Custom Card Component" icon-name="standard:lightning_component">
4    <div class="slds-p-around_medium">
5      <div class="slds-text-heading_small slds-m-bottom_medium">
6        Lightning Web Component
7      </div>
8
9      <p class="slds-text-body_regular slds-m-bottom_medium">
10        This component can receive messages from the host page and send responses back.
11      </p>
12
13      <div class="slds-m-bottom_medium">
14        <lightning-button
15          label="Send Message to Host Page"
16          variant="brand"
17          onclick="{handleSendMessage}"
18          class="slds-m-bottom_small"
19        >
20        </lightning-button>
21
22        <div class="slds-text-body_small slds-text-color_weak">
23          Click this button to test communication from LWC to the host page.
24        </div>
25      </div>
26
27      <div class="slds-text-heading_small slds-m-bottom_small">
28        Received Messages ({messages.length})
29      </div>
30
31      <template if:true="{messages.length}">
32        <div class="message-list">
33          <template for:each="{messages}" for:item="message">
34            <div key="{message.id}" class="message-item slds-box slds-m-bottom_small">
35              <div class="slds-text-body_regular"><strong>Message:</strong> {message.text}</div>
36              <div class="slds-text-body_small slds-text-color_weak">
37                <strong>Time:</strong> {message.timestamp}
38              </div>
39              <div class="slds-text-body_small slds-text-color_weak">
40                <strong>Source:</strong> {message.source}
41              </div>
42            </div>
43          </template>
44        </div>
45      </template>
46
47      <template if:false="{messages.length}">
48        <div class="slds-text-body_regular slds-text-color_weak">
49          No messages received yet. Click "Send Message to LWC" on the host page to start
50          communication.
51        </div>
52      </template>
53    </div>
54  </lightning-card>
55</template>

cardComponent.js 

Here’s the JavaScript for c-card-component. The connectedCallback() function runs when the component first loads. It initializes an event listener that responds to the sendMessageToLWC custom event defined on the host page. When the host page dispatches this event, the event listener calls the handleMessageFromHost() event handler. The event handler adds the received message to a list for the c-card-component to display.

c-card-component also has the handleSendMessage event handler, which responds to users clicking the component’s nested lightning-button. The event handler defines and dispatches the lwcMessagetoHost event to the host page.

1//cardComponent.js
2import { LightningElement, track } from "lwc";
3
4export default class CardComponent extends LightningElement {
5  @track messages = [];
6  @track messageCounter = 0;
7
8  connectedCallback() {
9    // Listen for messages from the host page
10    this.addEventListener("sendMessageToLWC", this.handleMessageFromHost);
11  }
12
13  disconnectedCallback() {
14    // Clean up event listeners
15    this.removeEventListener("sendMessageToLWC", this.handleMessageFromHost);
16  }
17
18  handleMessageFromHost = (event) => {
19    console.log("Message received from host page:", event.detail);
20
21    // Add message to our list
22    this.messageCounter++;
23    const newMessage = {
24      id: this.messageCounter,
25      text: event.detail.message,
26      timestamp: event.detail.timestamp,
27      source: event.detail.source,
28    };
29
30    this.messages = [...this.messages, newMessage];
31  };
32
33  handleSendMessage() {
34    console.log("Sending message to host page...");
35
36    const newMessage = `Message from LWC at ${new Date().toLocaleTimeString()}`;
37
38    const hostEvent = new CustomEvent("lwcMessageToHost", {
39      detail: {
40        message: newMessage,
41        timestamp: new Date().toISOString(),
42        component: "c-card-component",
43      },
44      bubbles: true,
45      composed: true,
46    });
47
48    this.dispatchEvent(hostEvent);
49    console.log("Message sent to host page");
50  }
51}

Host Page HTML 

Now let’s examine the host page. It has three main UI areas relevant to this example: the embedded c-card-component (1), a button that users click to send messages to c-card-component (2), and an area where messages from c-card-component will appear (3).

The host page after the Lightning Out 2.0 app is initialized, but before any custom events are dispatched

Here’s an HTML snippet of the host page. It includes:

  • The Lightning Out 2.0 JavaScript library.
  • A Lightning Out 2.0 app with a c-card-component web component.
  • A sendMessageButton button that dispatches a custom event to c-card-component when clicked. When the message is dispatched, the host page also shows a confirmation message.
  • An lwcMessageResponse element that shows the latest message that the host page receives from the embedded c-card-component.
1<!-- body of index.html-->
2<body>
3  <script
4    type="text/javascript"
5    async=""
6    src="https://MY_DOMAIN_NAME.my.salesforce.com/lightning/lightning.out.latest/index.iife.prod.js"
7  ></script>
8  <div class="main-wrapper">
9    <div class="responsive-container">
10      <lightning-out-application
11        app-id="18_DIGIT_SALESFORCE_ID"
12        components="c-card-component"
13        frontdoor-url=""
14      ></lightning-out-application>
15      <c-card-component></c-card-component>
16    </div>
17    <div class="container">
18      <h1>Lightning Out Example</h1>
19      <div class="communication-section">
20        <p>Send a message to the LWC component and see the response</p>
21        <button id="sendMessageButton" class="btn control-btn">
22          Send Message to LWC
23        </button>
24        <div id="messageResponse" class="response" style="display: none;">
25          <h4>Communication Response:</h4>
26          <div id="messageContent"></div>
27        </div>
28      </div>
29
30      <div class="lwc-message-section">
31        <p>Messages sent from the LWC component appear here</p>
32        <div id="lwcMessageResponse" class="response" style="display: none;">
33          <h4>Latest Message:</h4>
34          <div id="lwcMessageContent"></div>
35        </div>
36      </div>
37    </div>
38  </div>
39  <script src="script.js"></script>
40</body>

Host Page JavaScript 

Here’s a snippet of the host page’s script.js file.

To dispatch an event to the embedded c-card-component, an event listener is set on the sendMessageButton button. When a user clicks the button, the event listener calls an anonymous event handler. This event handler defines and dispatches the sendMessageToLWC event directly on the Lightning Out 2.0 web component c-card-component. The event is mirrored to the embedded LWC component c-card-component.

To receive the lwcMessageToHost event from the embedded c-card-component, set an event listener on the Lightning Out 2.0 web component c-card-component. Ensure that the Lightning Out 2.0 app and the c-card-component have loaded before setting this event listener. For example, set the event listener conditionally based on whether the frontdoor-url attribute has been set on the lightning-out-application. When the embedded c-card-component dispatches the lwcMessageToHost event, the event listener calls the handleLwcMessage function. This function displays the latest message from the embedded component on the host page.

To learn how to set the frontdoor URL at run time, see Set Up Authentication for Lightning Out 2.0 in Salesforce Help.

Tip

1// script.js
2
3// DOM elements
4const sendMessageButton = document.getElementById("sendMessageButton");
5const messageResponse = document.getElementById("messageResponse");
6const messageContent = document.getElementById("messageContent");
7const lwcMessageResponse = document.getElementById("lwcMessageResponse");
8const lwcMessageContent = document.getElementById("lwcMessageContent");
9const lightningOutApp = document.querySelector("lightning-out-application");
10
11// Obtain frontdoor URL and set its frontdoor-url attribute on the lightning-out-application to this value (OAuth flow and UI Bridge implementation not shown)
12// ... existing code ...
13if (result.success) {
14  // Set the frontdoor URL on the lightning-out-application component
15  if (lightningOutApp && result.frontdoorUrl) {
16    lightningOutApp.setAttribute("frontdoor-url", result.frontdoorUrl);
17
18    // Set up event listener for lwcMessageToHost event
19    const cardComponent = document.querySelector("c-card-component");
20    if (cardComponent) {
21      cardComponent.addEventListener("lwcMessageToHost", handleLwcMessage);
22    }
23  }
24}
25
26// Send message to LWC component
27sendMessageButton.addEventListener("click", function () {
28  console.log("Sending message to LWC component...");
29
30  // Wait for Lightning Out 2.0 to initialize, and then dispatch the event
31  setTimeout(() => {
32    const cardComponent = document.querySelector("c-card-component");
33
34    if (cardComponent) {
35      console.log("Found card component, sending message...");
36
37      // Create a custom event to communicate with the LWC component
38      const messageEvent = new CustomEvent("sendMessageToLWC", {
39        detail: {
40          message: `Hello from host page at ${new Date().toLocaleTimeString()}`,
41          timestamp: new Date().toISOString(),
42          source: "host-page",
43        },
44        bubbles: true,
45        composed: true,
46      });
47
48      // Dispatch the event to the card component
49      cardComponent.dispatchEvent(messageEvent);
50
51      messageContent.innerHTML = `
52                <strong>Status:</strong> Message sent<br>
53                <strong>Target:</strong> c-card-component<br>
54                <strong>Timestamp:</strong> ${new Date().toISOString()}
55            `;
56
57      messageResponse.style.display = "block";
58
59      console.log("Message sent to LWC component");
60    } else {
61      messageContent.innerHTML = `
62                <strong style="color: red;">Error:</strong> LWC component not found<br>
63                <strong>Message:</strong> Ensure Lightning Out is properly loaded
64            `;
65      messageResponse.style.display = "block";
66      console.error("LWC component not found");
67    }
68  }, 500);
69});
70
71// Handle lwcMessageToHost event
72function handleLwcMessage(event) {
73  console.log("Message received from LWC:", event.detail);
74
75  lwcMessageContent.innerHTML = `
76        <strong>Message:</strong> ${event.detail.message}<br>
77        <strong>Component:</strong> ${event.detail.component}<br>
78        <strong>Timestamp:</strong> ${event.detail.timestamp}
79    `;
80
81  lwcMessageResponse.style.display = "block";
82
83  // Add a visual indicator that a message was received
84  lwcMessageResponse.style.borderLeftColor = "#28a745";
85  lwcMessageResponse.style.background = "#d4edda";
86
87  // Reset the styling after 3 seconds
88  setTimeout(() => {
89    lwcMessageResponse.style.borderLeftColor = "#667eea";
90    lwcMessageResponse.style.background = "#f8f9fa";
91  }, 3000);
92
93  console.log("Message displayed in the host page.");
94}

Here’s the host page after a user dispatched three messages from the host page to c-card-component (1). Whenever the user dispatches a message to c-card-component, a confirmation message appears in the Messages to the LWC Component section (2). The user also dispatched messages from c-card-component to the host page. The most recent message appears in the Messages from the LWC Component section (3).

The host page after custom events are dispatched to and from the embedded LWC component

See Also