Session Replay and Heatmaps for Svelte and SvelteKit
Add session replay and heatmaps to a Svelte or SvelteKit application. Covers placing the loader in app.html, client-side navigation, identifying users, tracking events from the browser only, and masking.
Short answer: in SvelteKit, add the loader script tag to src/app.html, after %sveltekit.head%. In a plain Svelte app built with Vite, add it to index.html. Client-side navigation is recorded automatically. Call identify and track from browser-only code, and add masking classes to elements that show personal data.
Table of Contents
SvelteKit
app.html is the template every page is rendered into, so a tag placed there is on every page and loads once.
<!-- src/app.html -->
<head>
%sveltekit.head%
<script async src="YOUR_LOADER_URL"></script>
</head>Your loader URL is in the project's install settings. Reference: Install in Angular, Svelte, or SvelteKit.
This works the same for server-rendered, prerendered, and client-rendered routes. The recorder runs in the browser on the final DOM.
Svelte With Vite
For a client-only Svelte app, add the same tag to the project's index.html:
<head>
<script async src="YOUR_LOADER_URL"></script>
</head>Navigation
SvelteKit's router uses the History API for client-side navigation. The SDK patches it, so each navigation becomes a page view in session timelines, heatmaps, and funnels. No afterNavigate hook is needed.
For interface changes that do not alter the URL, such as steps in a form, track an event.
Calling the SDK Safely
SvelteKit runs your code on the server as well as in the browser. The SDK exists only in the browser, so guard your calls.
import { browser } from "$app/environment";
export function track(event, props) {
if (browser && typeof window.deepsync === "function") {
window.deepsync("track", event, props);
}
}
export function identify(id, traits) {
if (browser && typeof window.deepsync === "function") {
window.deepsync("identify", id, traits);
}
}Identify once the user is known, from a layout or after login:
identify(user.id, { plan: user.plan });Track confirmed outcomes:
track("checkout_completed", { value: 2499, currency: "INR" });If a call can happen before the loader finishes, add the command-queue stub from how the SDK works to app.html above the loader, so early calls are held and sent once the SDK is ready.
See the SDK API for all calls.
Masking
Inputs, passwords, and payment fields are masked by default. Add classes for rendered content:
<p class="ds-mask">{customer.phone}</p>
<div class="ds-block">
<StatementTable rows={statement} />
</div>Svelte's scoped styles do not interfere. The recorder reads the class from the DOM (PII masking).
In consent-required mode, which is the default, send the consent signal from your cookie banner (consent modes).
Things to Check
- Transitions. Svelte's
transition:directives animate elements in. A recording may briefly show the starting state. If content appears missing in replay, check for an entrance animation (capture limits). - Prerendered pages. These are static HTML. The tag in
app.htmlis included, so they are recorded. - Form actions. SvelteKit form actions with progressive enhancement submit without a full reload. Track success as an event from the action's result.
- Hydration gap. Server-rendered pages are visible before they are interactive. Dead clicks in that window are worth noticing.
- Content Security Policy. If you configure one, allow the ingest origin in
script-srcandconnect-src(CSP guide). - Preview deployments. Avoid recording preview and staging builds in your production project (excluding internal traffic).
Key Takeaways
- One script tag in
src/app.html. - Navigation is tracked automatically.
- Guard SDK calls so they run only in the browser.
- Mask rendered personal data with a class.
Conclusion
SvelteKit setup is one line in one file. After that, the work is deciding what to mask and which outcomes to track.
Frequently Asked Questions
Related articles
Stay in the loop
Get the latest insights on product analytics and user behavior delivered to your inbox.



