To add live chat to SvelteKit, you load one asynchronous script in the browser, once, from a place that stays mounted while visitors move between routes. In practice that means either a script tag in src/app.html or a small block of code in the root +layout.svelte. There is no package to install, and the whole job usually takes about ten minutes. This guide shows both setups with code, explains how to keep the site key in an environment variable, and covers the SvelteKit-specific details that cause trouble: server rendering, client-side navigation, prerendered pages and content security policy.
The examples use Talkmio, where the widget is a single script that loads from your site key. The same approach works for most chat widgets, but the key and URL below are specific to Talkmio.
What You Need Before You Start
Create an account at app.talkmio.com; the Free plan needs no card. Open Websites, add your site and copy the 12-character site key. That key is public by design, because it appears in the page source of every visitor, so it is configuration rather than a secret. If you have a staging site, add it as a second website with its own key so that test conversations never mix with real customers.
You also need a SvelteKit project that you can build and preview locally. The steps work with any adapter, whether you deploy to a Node server, a static host or a serverless platform, because the chat script runs in the visitor’s browser and not in your server code. If you need the same setup for other frameworks, our guides for Next.js and React and for Vue and Nuxt follow the same logic with different file names.
Why SvelteKit Needs a Little Care
A classic website reloads every page, so a script before the closing body tag runs again on each navigation. SvelteKit behaves differently. After the first load, the router takes over in the browser and swaps components without a full page reload. That is fast for visitors, but it means a chat script has to be loaded once and left alone. Three properties of the framework matter:
- Server-side rendering. Components render on the server first, where there is no
windowordocument. Code that touches the browser must run only after the component has mounted. - Client-side navigation. The root layout stays on screen while child pages change. A script added from a page component can be duplicated or lost; a script added from the root layout or the HTML template is loaded one time.
- Prerendering. Prerendered pages are plain HTML files, so anything you add to the template ends up in each of them.
With those three in mind, the two options are straightforward. Pick one and stay with it.
How to Add Live Chat to SvelteKit in app.html
The simplest option is the HTML template. Every SvelteKit project has a file called src/app.html that wraps every page, as the official project structure documentation describes. Open it and paste the Talkmio snippet before the closing body tag, after the placeholder for the page body:
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
<script src="https://app.talkmio.com/w/YOUR_SITE_KEY.js" async></script>
</body>
Replace YOUR_SITE_KEY with your own key. Your template may differ slightly, for example if you changed the preload option, so keep your existing attributes and add only the script line. Restart the dev server, open the site and the chat bubble should appear in the corner.
This works because the template is served once with the first page. SvelteKit’s router never reloads it during client-side navigation, so the widget stays loaded and keeps the visitor’s conversation open as they browse. The script is asynchronous, so it does not block rendering.
The trade-off is that the key is hard-coded in the template. That is fine for a single site, but if you use a different key for staging, you need a build step that swaps the value, which brings us to the second option.
Loading the Widget from the Root Layout
The second option puts the loader in src/routes/+layout.svelte, the root layout that wraps every page. It reads the key from a public environment variable and appends the script in the browser after the layout mounts:
<script>
import { onMount } from 'svelte';
import { PUBLIC_TALKMIO_KEY } from '$env/static/public';
onMount(() => {
if (document.getElementById('talkmio-widget')) return;
const s = document.createElement('script');
s.id = 'talkmio-widget';
s.src = `https://app.talkmio.com/w/${PUBLIC_TALKMIO_KEY}.js`;
s.async = true;
document.body.appendChild(s);
});
</script>
<slot />
Three details make this safe. onMount runs only in the browser, so server rendering never touches document. The id check stops a second copy from being added if the layout is ever remounted. And the PUBLIC_ prefix is the SvelteKit convention for variables that are allowed to reach the browser. In a Svelte 5 project that uses snippets, replace the slot line with your existing children rendering and keep the rest unchanged.
Add PUBLIC_TALKMIO_KEY to your .env file locally and to the environment settings of your host. Each environment can then use its own key without any change to the code.
Which Option Should You Choose?
Both options end with the same widget. The table summarises the practical differences.
| Question | Script in app.html | Loader in +layout.svelte |
|---|---|---|
| Effort | One line | About fifteen lines |
| Key per environment | Needs a build step | Public environment variable |
| Works on prerendered pages | Yes, in the template | Yes, after hydration |
| Wait for consent | Not directly | Easy to gate with a condition |
| Risk of double loading | None | None, thanks to the id check |
If you have one site and no consent banner, use app.html. If you have staging and production, or you must wait for cookie consent before loading third-party scripts, use the layout loader.
Identify Signed-In Users
If your SvelteKit app has accounts, your team will find chats more useful when they see who is writing. Talkmio reads a small global object with the name and e-mail of the signed-in user, which must be set before the widget script loads:
window.__TALKMIO_USER__ = { name: 'Anna Example', email: '[email protected]' };
In the layout loader, set this inside onMount just before you append the script, using the user from your page data. If the user signs in after the page has loaded, a reload after login is the simplest way to make the name appear in the Inbox. Never put anything sensitive in this object, and tell agents in your instructions never to ask visitors for passwords or payment details in chat.
Cookie Consent and Privacy
If you gate third-party scripts behind a consent banner, do not paste the script into app.html. Use the layout loader and wrap the append step in a check on your consent state, so that the widget appears only after the visitor agrees. Our guide to chat widgets, cookies and consent explains what to consider, and your own legal adviser should confirm what applies to your audience.
Talkmio stores data on servers in the EU (Germany). Only the conversation text and relevant knowledge base excerpts go to the AI model provider, and you can export or delete your data at any time. Mention the chat in your privacy notice so visitors know what happens to their messages.
Content Security Policy and Preview Builds
A content security policy tells the browser which sources it may load scripts from. SvelteKit can set one through the kit.csp option in svelte.config.js. If you use it, allow scripts and connections from app.talkmio.com, or the browser will block the widget and the bubble will not appear. Check the browser console on a production build: a blocked script is reported there with the directive that caused it.
Preview deployments get their own addresses, and their traffic should not appear as real conversations. Use the staging site key there, set through the environment variable described above. It also helps to test on a built version with vite preview rather than only in development, because the dev server hides problems that appear after bundling.
Page Speed and Mobile Checks
The widget loads asynchronously and is small, but it is still an extra request, so measure your pages before and after with the tool you prefer. Our guide to live chat widget page speed explains what to look at. On a phone, open the site and confirm that the bubble does not cover a primary button or a cookie banner. If it does, change the position in the Talkmio widget settings instead of editing your Svelte layout. Changes in the widget settings apply within five minutes.
Teach Mio Before You Go Live
A chat bubble that answers badly is worse than none. Open Mio AI, then Knowledge base, and add your website so that Mio reads every public page on the domain. This is where SvelteKit helps you: server-rendered and prerendered pages contain their text in the HTML, so Mio can read them without running scripts. Add business instructions that say what you sell, the tone to use and what Mio must never promise, then check real questions with Try Mio. Our guide to testing an AI chatbot before launch gives a practical question list.
Troubleshooting
If the bubble does not appear, check four things in order. Is the key correct and present in the environment of the deployed build? Does the page source or the inspector show the script tag? Does the console report a blocked request from a content security policy or an ad blocker? Is the script loaded twice, for example from both app.html and the layout? Fixing the first one that fails solves most cases. If all four look fine, test in a private window with extensions disabled, because privacy extensions are the most common reason a widget is invisible on one machine and fine on another. Also compare the key you pasted with the one shown in Talkmio under Websites, since a single mistyped character produces a script that loads nothing.
Frequently Asked Questions
Do I need an npm package to add live chat to SvelteKit?
No. The Talkmio widget is a single script that loads from your site key, so you can paste it into src/app.html or append it from the root layout. A package would only add a dependency to maintain. Keep the key in a public environment variable if you use different sites for staging and production.
Will the chat script break server-side rendering?
Not if it runs only in the browser. A script tag in app.html is plain HTML, and a loader in +layout.svelte should run inside onMount, which executes after the component reaches the browser. Avoid touching document or window at the top level of a component, because the server has neither.
Does the widget reload when visitors navigate between pages?
No. SvelteKit’s client-side router swaps page components without reloading the document, so a script loaded once from app.html or the root layout stays active. The conversation remains open as visitors browse. A full reload, such as a hard link to an external page and back, loads it again.
How do I use a different site key for staging?
Create a second website in Talkmio with its own key, then store each key in a PUBLIC_ environment variable per deployment. The layout loader reads that variable at build time. This keeps test conversations and reports separate from real customers, and it needs no code change between environments.
Can I show the widget only after cookie consent?
Yes. Use the layout loader instead of the script in app.html, and run the append step only when your consent state allows third-party scripts. If the visitor later withdraws consent, remove the script and reload. Ask your legal adviser which categories your consent banner needs to cover.
Can Mio read my SvelteKit pages?
Yes, when the text is in the HTML. Mio reads public pages on the domain you add and respects robots.txt, so server-rendered and prerendered pages work well. Content that appears only after client-side scripts run may be missed, so add key pages as separate sources or Q&A pairs.
The Bottom Line
To add live chat to SvelteKit, paste one script into src/app.html or load it from the root layout with onMount, keep the site key in a public environment variable, allow app.talkmio.com in any content security policy and test a built version. Then give Mio good pages to learn from and check its answers before visitors see them. Create a free Talkmio account, copy your site key and have the widget running in your SvelteKit project today.
