HTML panels

Using HTML panels, you can extend TalkJS to include anything from product details or lead collection forms to credit card payments. An HTML panel places an HTML document in an iframe in your chats, just above the message field.

Example of an email sign-up form created using an HTML panel

Usage

You can create an HTML panel by making a createHtmlPanel call on a UI element. This call accepts a few parameters such height of the panel, allowing to customize the experience further. The createHtmlPanel call returns a Promise, which resolves when the onload event of the underlying iframe fires, returning the frame's window object for easier interaction between the page in which the chat is embedded, and the HTML panel's page.

The JavaScript SDK makes interacting with your HTML panels easier by passing you the window element of the requested URL in a promise when createHtmlPanel is called. But because of browser's security restrictions, when you try to access a panel's window variable, it's subject to the same-origin policy. This means that if you are on another domain, most of the properties of window will not be accessible. In this case, cross-domain communication can still be achieved with window.postMessage().

To avoid any issues with same-origin policy browser security restrictions, we recommend that you host this HTML document on the same domain (and subdomain) as your main site. Some of the example code below, for instance, will only work if you have both your main app and the embedded HTML document hosted on the same domain (https://www.mywebsite.com).

1// Create chat UI as shown in previous examples
2const inbox = session.createInbox();
3inbox.mount(document.getElementById('inbox-container'));
4
5// Then call `createHtmlPanel`, which returns a Promise that's resolved with control methods
6const htmlPanel = await inbox.createHtmlPanel({
7 url: 'register-form.html', // or an absolute url: "https://mywebsite.com/frames/register-form.html"
8 height: 300,
9 show: true,
10});

When the panel is ready

The HTML panel object exposes two promises that reflect the lifecycle of the window. You can, for example, use these to attach event handlers to controls inside the HTML panel.

1const htmlPanel = await inbox.createHtmlPanel({
2 url: 'register-form.html', // or the absolute path "https://www.mywebsite.com/frames/register-form.html"
3 height: 300,
4 show: true,
5});
6
7await htmlPanel.DOMContentLoadedPromise;
8// ... do work that includes manipulation of the DOM.
9const form = htmlPanel.window.document.getElementById('registration-form');
10form.submit();
11
12await htmlPanel.windowLoadedPromise;
13// ... do work that requires all assets (including images, CSS, scripts, and the like) of
14// the panel to be fully loaded.

Show or hide the panel

You can hide or show an HTML panel at any time. This is helpful when you want to preload the page or build interactive experiences.

1htmlPanel.show();
2htmlPanel.hide();

You can also call htmlPanel.isVisible to check the visibility of your panel.

Resize the panel

You can set the height of the panel either when creating it with createHtmlPanel or by using htmlPanel.setHeight(). You should always pass a number, which is then interpreted as pixels.

1htmlPanel.setHeight(400);

Different HTML panels for different conversations

By default, an HTML panel is shown for all conversations, even if the user switches to a different conversation (by clicking in the feed in the Inbox, or when the select method is called).

You can also limit an HTML panel to a single conversation. This means that the panel will be shown only when that conversation is displayed:

1const htmlPanel = await inbox.createHtmlPanel({
2 url: 'https://www.mywebsite.com/frames/register-form.html',
3 height: 300,
4 show: true,
5 conversation: myConversation, // <-- that's the one
6});

conversation can be either a string ID or a conversation object as returned by getOrCreateConversation.

You can call createHtmlPanel multiple times this way, to immediately load multiple panels for different conversations. TalkJS will preload every HTML panel you create, but keep them hidden until the appropriate conversation is selected.

If you omit conversation in one createHtmlPanel call but not in others, then the panel without a given conversation will act as the default HTML panel. It will be shown for conversations that have no conversation-specific HTML panel configured.

Interactive HTML panels

You can hook up code or events that happen inside an HTML panel with things happening in the rest of your app. Because the HTML panel is a separate iframe, it has its own Window object and its own DOM. But because your app code is served from the same origin (see below), the browser lets your code access all internals of the HTML panel iframe.

This means that you can load some JavaScript code inside the HTML panel and have it call into your app's Window object, or you can just instrument the HTML inside the HTML panel from your main app code.

Respond to events inside the HTML panel

Let's say our HTML panel has this code:

1<button id="say-hi-button">Say Hi</button>

Then we can set a custom message inside the message field with code like this:

1const inbox = session.createInbox(...);
2const htmlPanel = await inbox.createHtmlPanel(...);
3
4// wait until the HTML is loaded
5await htmlPanel.DOMContentLoadedPromise;
6
7// register an event handler like usually, just via `htmlPanel.window`
8const button = htmlPanel.window.document.getElementById("say-hi-button");
9button.addEventListener("click", () => {
10 inbox.messageField.setText("Hi");
11});

You can take this technique quite far if you want to. For example, if you're building a React app, you can load a tiny .html file into an HTML panel with only a single empty top-level <div>. Then, use React Portals to render HTML staight into that div, directly from your main application code.

Call your app from inside the HTML panel

An alternative way to accomplish the same, is to call into your host app window from inside the HTML panel:

1// assign the inbox to the host frame's Window object
2window.inbox = session.createInbox(...);
3const htmlPanel = await inbox.createHtmlPanel(...);

And then, inside the HTML panel, access the host app's Window object using window.top:

1<button id="say-hi-button">Say Hi</button>
2<script>
3 var button = document.getElementById("say-hi-button");
4 button.addEventListener("click", function() {
5 window.top.inbox.messageField.setText("Hi");
6 });
7</script>

Note that window.top is the top frame, which corresponds to your app's Window object (unless your app itself is inside an iframe). You can't use window.parent here, because that's the TalkJS UI itself and the browser's security rules won't let you access it.

Limitations

Due to browser security policies, your HTML panels must be hosted on HTTPS. This means that:

  • You can only use HTML panels if you can host the HTML panel content on HTTPS.
  • To be able to interact with the HTML panel client-side with JavaScript, the panel needs to be on the "same origin". Browsers consider http://example.com and https://example.com to be different origins, so that means that your entire app needs to be hosted on HTTPS if you need this.
  • You also need to host the HTML panel content on HTTPS during development.

To make developing and testing HTML panels more smooth, you can use a tool such as ngrok. Ngrok lets you serve the HTML panel pages from your local development server over HTTPS, by giving it a special custom URL such as https://qwerty12345.ngrok.io which points straight to your local development server.

Your server must not send an X-Frame-Options header with HTML panel pages, as HTML panels are loaded into iframes. Even if your page that contains the chat is on the same origin as the HTML panel page, you it still won't work, even with the header's value set to sameorigin, because the TalkJS UI is loaded in an iframe on a TalkJS domain, which has a different origin.

You can use the Content-Security-Policy header to achieve something similar:

1Content-Security-Policy: frame-ancestors 'self' https://app.talkjs.com;

Resources