HTML Panels

An HTML Panel allows you to place an HTML document in an iframe in your chats, just above the message field. Using HTML Panels, you can extend TalkJS UIs to have anything from credit card payments to lead collection forms, or, for instance, to show the product details of a marketplace transaction between your users. You can also check out our tutorial on how to make an interactive in-chat questionnaire using HTML Panels.

Usage

HTML panels can be created for all UI options by simply making a createHtmlPanel call on the UI element. This call accepts a few parameters like height of the panel, allowing to customize the experience further. The createHtmlPanel call will return a Promise, which will resolve 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 JS SDK also makes interacting with your HTML panels easy 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().

The email form in the pop-up shown below is an example of an HTML panel

We recommend that you host this HTML document on the same domain (and subdomain) as your main site. This is because browsers have a security feature called the Same-Origin Policy which severely limits the kind of programmatic interaction you can do with iframes hosted on different domains. 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 use these to e.g. 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, etc) of
14// the HTML Panel to be fully loaded.

Visibility

You can hide or show the 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.

Resizing the panel

You can also 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).

But 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

It's possible to hook up code or events that happen inside an HTML Panel, with the things that happen 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 will let 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.

Responding 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.

Calling 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.

HTML Panel Limitations

Due to browser security policies that are impossible to circumvent, your HTML Panels must be hosted on HTTPS. This means that:

  • If it is not possible for you to host the HTML Panel content on HTTPS, then you cannot use HTML Panels.
  • As described above, to be able to interact with the HTML Panel client-side with JavaScript, it needs to be on the "same origin". Browsers consider http://example.com and https://example.com to be different origins, so 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, which can be a bit cumbersome.

One effective way to make developing and testing HTML Panels less cumbersome is to use a tool like 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. The free version is usually more than capable enough for this.

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:

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

Read more