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.
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 examples2const inbox = session.createInbox();3inbox.mount(document.getElementById('inbox-container'));45// Then call `createHtmlPanel`, which returns a Promise that's resolved with control methods6const 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});
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.
- DOMContentLoaded: the DOM has been loaded
- window.onload: the document has been fully loaded, including things such as images
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});67await htmlPanel.DOMContentLoadedPromise;8// ... do work that includes manipulation of the DOM.9const form = htmlPanel.window.document.getElementById('registration-form');10form.submit();1112await htmlPanel.windowLoadedPromise;13// ... do work that requires all assets (including images, CSS, scripts, and the like) of14// the panel to be fully loaded.
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.
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);
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 one6});
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.
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.
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(...);34// wait until the HTML is loaded5await htmlPanel.DOMContentLoadedPromise;67// 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.
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 object2window.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.
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
andhttps://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;
- Tutorial How to make an interactive in-chat questionnaire using HTML panels
- Learn about all options you can pass as options for HTML panels.
- Learn about methods for
HtmlPanel
instances. - More about the Same-Origin Policy on MDN (Mozilla Developer Network)