0 cookiesnothing loads before a clickunlimited teammatesreply from slack, discord or your phone<4kb launcherno per-seat tax

/blog / guides

add live chat to a plain html site

· by kuba · 6 min read

A terminal window with a code bracket symbol in green, the Spookat cat peeking over its top edge.

A plain HTML site needs exactly one line to get live chat: a script tag before </body>. This guide shows where that line goes on hand-written pages and on sites built with a static generator, every option you can set next to it, and how to check in the browser that it loads nothing before a click.

the line

<script src="https://cdn.spookat.com/s/YOUR_SITE_KEY.js" async></script>

Your site key is on the dashboard’s install page, already filled into the snippet with a copy button. It starts with site_live_ and it’s public: it’s in your page source anyway, and it only works on the domain you registered.

The file this loads is the launcher: a button, its styles, and the look you published in the dashboard. It is under 4 kb gzipped (the build fails above that). It makes 0 requests to our API and sets no cookies. The chat panel and the connection to our server load only when a visitor clicks.

New to all this? The pillar guide explains what a chat widget costs a page and what to look for.

where it goes

Right before the closing </body> tag:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>My site</title>
    <link rel="stylesheet" href="/style.css">
  </head>
  <body>
    <h1>Hello</h1>
    <p>Everything else on your page.</p>

    <script src="https://cdn.spookat.com/s/YOUR_SITE_KEY.js" async></script>
  </body>
</html>

Why there, and why async:

  • At the end of the body, the tag doesn’t hold up anything above it. The browser has already seen your content by the time it reaches the tag.
  • async tells the browser to fetch the file in the background and run it when it arrives, without pausing the page. Keep it.

every page, once

The chat should be on every page a visitor might have a question on, which is usually all of them. How you get it there depends on how the site is made.

Hand-written pages. Paste the tag into each .html file. Search the folder for </body> to find them all. When you add a page later, copy an existing one as the starting point and the tag comes along.

A shared footer. If your pages include a common footer file (a server-side include, a PHP include, a template partial), put the tag there once.

Static site generators. Put it in the base layout, the template every page extends:

generator file
Jekyll _layouts/default.html, or _includes/footer.html
Hugo layouts/_default/baseof.html, or a footer partial
Eleventy your base layout in _includes/
Astro the base layout component
Pandoc, custom scripts the HTML template you pass in

Don’t worry about a page that ends up with the tag twice: the launcher checks whether it’s already on the page and the second copy does nothing. It’s still a wasted request, so keep one.

setting it up in code

The look you pick in the dashboard travels inside the launcher, so the one line above is the whole install. If you’d rather keep the settings in your HTML, set window.Spookat in a script above the tag:

<script>
  window.Spookat = {
    style: "clean",
    font: "system",
    accent: "#2F6FEB",
    side: "right",
    greeting: "hi. questions about an order? ask here.",
    lang: "en"
  };
</script>
<script src="https://cdn.spookat.com/s/YOUR_SITE_KEY.js" async></script>

If the page and the dashboard disagree, the page wins. What each key does:

key what it takes does
style one of 8 styles the look. brutal, clean, glass, soft, terminal, paper, retro, swiss
font "system" or one of 17 hosted fonts the widget’s font. system downloads nothing; hosted fonts load from our CDN after the click, never from Google
accent "#RRGGBB" your brand color. The text on it is picked for contrast
side "right" or "left" where the launcher sits
greeting text, up to 200 characters the first message a visitor sees. Empty means the style’s own
label text, up to 40 characters turns the round button into a pill with this text
lang a language code, like "en" or "pl" the widget’s language. Default: the visitor’s browser, then English
user { id, sig }, on cool guy and up who is logged in, signed on your server

The docs have the full reference, including how to sign user.

a multilingual site

If your site has one folder per language (/en/, /pl/), set lang per folder so the widget matches the page, not the browser. The widget speaks 12 languages. Your team sees the visitor’s language on each chat.

one launcher that says something

A plain site often has one clear question visitors ask: “do you ship to…”, “can I book…”. A label like "questions? ask a human" turns the icon into a small pill that says so. Keep it short; it sits in the corner on phones too.

what the visitor gets

A button in the corner, in your colors. Clicking it opens the panel with your greeting and a line that a human answers. They type; the message lands in your Slack or Discord thread, or in our inbox and its phone app; your reply comes back into the widget. If they leave before you answer and gave an email, cool guy and up send them the reply by email. The chat stays on their device through page changes and later visits, until the history expires (30 days on broke af).

verify it

Open your site in a private window, so no old cache or storage is involved. Then open DevTools (F12, or right click and Inspect).

  1. Network. Tick “Disable cache”, reload, and type spookat into the filter. One request: the launcher from cdn.spookat.com, around 2 kb transferred. Two, both from cdn.spookat.com, if your code sets a style in window.Spookat that isn’t the one you published. Nothing to api.spookat.com.
  2. Application (Storage in Firefox). Cookies: nothing from Spookat. Local storage: nothing yet. One key appears only after you send a first message; it keeps your chat on that device.
  3. Click the launcher. Now the panel script and your style’s stylesheet load from the CDN, then the chat connects. Send a test message and see it arrive in Slack or Discord.

If the launcher doesn’t show:

  • Nothing in the Network tab. The tag isn’t on this page, or it’s inside an HTML comment, or your build step removed it. View the page source and search for spookat.
  • The request fails. A key of the wrong length gets a 404 from the CDN. Check the key in the URL against the dashboard.
  • The launcher shows in the default look and never connects. One wrong character in the key still loads a launcher. Check the key against the dashboard.
  • The launcher shows, but the chat doesn’t connect. You’re on a different domain than the one registered: a local file, localhost, or a staging host. The chat connects only on your domain and its subdomains.
  • A Content Security Policy blocks it. The console names the directive. Allow https://cdn.spookat.com for scripts, styles and fonts, wss://api.spookat.com for the connection, and data: for images, since logos and teammate pictures are data: images. The launcher also injects one small <style> block into its own shadow root, so style-src has to allow inline styles for it. If you set window.Spookat in an inline script, give that script your policy’s nonce or hash.

what not to add

Plain sites are fast because nothing is in the way. Keep it that way:

  • No tag manager just for the chat. It’s a script that loads scripts, and it weighs more than the launcher.
  • No preload or prefetch for the panel. It would move the panel’s download in front of the click, for every visitor.
  • No defer plus async juggling. async alone is right for this tag.
  • No wrapper that opens the chat after a few seconds. It loads the whole panel for everyone and covers your page on phones. A greeting does the same job when someone actually opens it.

hosting doesn’t matter

GitHub Pages, Netlify, Cloudflare Pages, an FTP folder on shared hosting, a server in a cupboard: the tag is plain HTML, so it works wherever your files are served from. No build step, no package, no server code.

connect the replies

Last step: connect Slack or Discord in the dashboard and pick a channel, or skip it and answer in our inbox and its phone app. Every visitor gets their own thread, anyone in the channel can reply, and there are no seats. It costs $4.99 a month per site on broke af.

checklist

  • One tag, with async, before </body>, on every page (or once in the shared layout).
  • Optional window.Spookat above it, if you keep the settings in code.
  • Checked in a private window: one request before the click (two, both from cdn.spookat.com, if your code sets a style you didn’t publish), no cookies.
  • A test message from the widget reached Slack or Discord.

The trial is 14 days and needs no card. Paste the line, and your static site can answer questions.

liked it? get in before launch.

invites go out in small batches. then 14 days free, no card.