Add a Chatbot to Your Website With an iframe

2026-10-28

The goal: a chatbot that already works on its own address shows up on your website, either as a fixed box inside a page or as a round button in the corner that opens it. Nothing here depends on one particular bot builder. Any chat that lives at a normal URL can be embedded this way, as long as it allows it.

What you need before starting:

  • the full URL of the chat page, called CHAT_URL below
  • edit access to your site's HTML or theme footer
  • if you control the chat server too, access to its response headers

Step 1. Check that the chat page can be framed

Do this first, because it decides everything else. Open the chat URL in a browser, then look at the response headers in the network panel. Two headers can forbid framing.

X-Frame-Options: DENY blocks all framing. X-Frame-Options: SAMEORIGIN allows only pages from the same origin as the chat. MDN also lists ALLOW-FROM, but marks it obsolete: modern browsers ignore the header when they meet it. The newer and more flexible control is Content-Security-Policy: frame-ancestors, which accepts a list of hosts.

If the chat is on a different domain from your site, SAMEORIGIN will block you. The fix belongs on the chat server, not on your page:

Content-Security-Policy: frame-ancestors 'self' https://www.your-site.example

As of October 2026, per the MDN page on frame-ancestors, several origins go into one directive separated by spaces, and the directive does not work from a <meta> tag. It must be an HTTP header. It also does not fall back to default-src, so a policy with default-src 'none' still lets anyone embed the page unless frame-ancestors is set.

This also answers a common worry: a bot you embed cannot be quietly copied onto another site if the list names only yours. Being able to restrict embedding to your own domains is the reason to prefer frame-ancestors over leaving the header out.

Step 2. The plain iframe

For a chat box that sits inside a page, for example a help page, this is enough:

<iframe
  src="CHAT_URL"
  title="Chat with our assistant"
  width="400"
  height="600"
  loading="lazy"
  allow=""
  style="border:0; max-width:100vw;">
</iframe>

The attributes matter more than they look. According to MDN's iframe reference, the default size without width and height is 300 by 150 CSS pixels, far too small for a chat. title gives screen reader users a label; without it they have to enter the frame to find out what it holds. loading="lazy" is a native attribute and needs no script of its own: it delays the load until the frame nears the viewport, which keeps a heavy chat from slowing the page. MDN adds that, as of October 2026, loading is deferred only when JavaScript is enabled in the browser (an anti-tracking measure); with scripting off, the frame loads at once.

An empty allow attribute adds no extra permissions. Fill it only if the bot genuinely needs something such as a microphone.

A frequent mistake here is copying the snippet into a page builder that strips unknown attributes or wraps it in a fixed-height block. If the box shows but is clipped, inspect the parent element before touching the iframe.

Step 3. The floating button

For a chat that follows the visitor, use a button and an iframe that is created only after the first click. Creating it on demand means nobody downloads the chat until they ask for it.

<button id="chat-toggle" aria-expanded="false" aria-controls="chat-panel">Chat</button>
<div id="chat-panel" hidden></div>

<style>
  #chat-toggle {
    position: fixed; right: 16px; bottom: 16px; z-index: 1000;
    padding: 12px 18px; border: 0; border-radius: 999px;
    background: #1f4fd8; color: #fff; font: 600 15px system-ui, sans-serif;
    cursor: pointer;
  }
  #chat-panel {
    position: fixed; right: 16px; bottom: 72px; z-index: 1000;
    width: 380px; height: 560px; max-height: calc(100dvh - 96px);
    border-radius: 12px; overflow: hidden;
    box-shadow: 0 8px 30px rgba(0, 0, 0, .25);
  }
  #chat-panel { display: flex; }
  #chat-panel[hidden] { display: none; }
  #chat-panel iframe { flex: 1; border: 0; }
  @media (max-width: 480px) {
    #chat-panel { right: 0; left: 0; bottom: 0; width: auto;
      height: 100dvh; max-height: none; border-radius: 0; }
  }
</style>

<script>
  (function () {
    var btn = document.getElementById('chat-toggle');
    var panel = document.getElementById('chat-panel');
    var loaded = false;
    btn.addEventListener('click', function () {
      var open = panel.hasAttribute('hidden');
      if (open && !loaded) {
        var f = document.createElement('iframe');
        f.src = 'CHAT_URL';
        f.title = 'Chat with our assistant';
        panel.appendChild(f);
        loaded = true;
      }
      if (open) { panel.removeAttribute('hidden'); } else { panel.setAttribute('hidden', ''); }
      btn.setAttribute('aria-expanded', String(open));
    });
  })();
</script>

On a phone the panel turns into a full-screen sheet. Note that on full screen the button is covered; add a close control inside the chat page, or move the toggle above the panel with a higher z-index.

Step 4. Fix the height on phones

100vh is the usual culprit when a chat input disappears under the mobile browser bar. As of October 2026, MDN says vh equals lvh, the large viewport, which is the size when the address bar is hidden. When the bar is showing, content sized that way can be covered by it. The dvh unit follows the bar as it shows and hides, so the panel always fits the visible area. MDN also warns that dvh values are not stable and can cause resizing while the user scrolls, which is acceptable for a chat sheet but would be annoying for a whole page layout. All of this comes from MDN's page on CSS length units.

Keep a vh line before the dvh one as a fallback if you need older browsers.

What usually goes wrong

  • Blank gray box, console says refused to display. Step 1. A framing header on the chat server names a different origin.
  • Chat loads, but is cut at the bottom. The parent container has a fixed height or overflow: hidden, or the page uses vh units on phones.
  • Button hidden behind the site's header or cookie banner. Raise z-index, or move the button to the left edge.
  • Chat remembers nothing between pages. An iframe is a separate page. Persistence is the chat server's job, usually through cookies or storage, and strict third-party cookie settings in some browsers may block it.

Check that it works

Load the page in a private window with no login, open the chat, and ask something your documents answer and something they do not. Then open the page from a site that is not on your frame-ancestors list, for example a local test file, and confirm the frame stays blank.