Install Flawsnap in Svelte and SvelteKit
In SvelteKit the widget goes in src/app.html, the template every page is rendered into, so it loads once and follows client-side navigation.
Steps
- 1
Allow your hosts
In Flawsnap, add your production and staging domains, plus
localhost:5173forvite devandlocalhost:4173forvite preview, under Security → Allowed domains. - 2
Add the snippet to the template
SvelteKit: paste it into the
<head>ofsrc/app.html, above%sveltekit.head%. Plain Svelte with Vite: paste it into the<head>ofindex.html. - 3
Declare the global
Add the
Windowdeclaration tosrc/app.d.ts(or a.d.tsof its own). - 4
Identify signed-in users
In your root
+layout.svelte, callwindow.Flawsnap?.identify()from an effect when layout data has a user. - 5
Test it
Run the dev server, send a test report and check it in the project.
SvelteKit: src/app.html
Every server-rendered and prerendered page uses this template, so one tag covers the whole app. Do not add it with <svelte:head> in a page: that ties it to one route and would load it again as people navigate.
SvelteKit's client-side router changes pages with the History API. Flawsnap follows those changes on its own, so reports carry the right URL and Insights (Pro) sees each route as a screen.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://backend.flawsnap.com/widget.js" data-key="pk_…" async></script>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>Plain Svelte with Vite
Without SvelteKit there is no app.html. Put the same tag in the <head> of the index.html at the project root, next to the <script type="module"> that mounts your app.
Identify the user from layout data
If your root +layout.server.ts returns the user, the layout can pass it to Flawsnap. Effects only run in the browser, so window is always there.
<script lang="ts">
let { data, children } = $props()
$effect(() => {
if (data.user) {
window.Flawsnap?.identify({ name: data.user.name, email: data.user.email })
}
})
</script>
{@render children()}On Svelte 4, use onMount together with a reactive statement ($: if (browser && data.user) …) with browser from $app/environment.
The global declaration
// flawsnap.d.ts (anywhere your tsconfig includes)
export {}
declare global {
interface Window {
Flawsnap?: {
open(): void
identify(reporter: { name?: string; email?: string }): void
setMetadata(details: Record<string, unknown>): void
screen(name: string): void
track(name: string): void
resetUser(): void
}
}
}If you use kit.csp
SvelteKit can generate a Content Security Policy from kit.csp in svelte.config.js. With one in place, the browser blocks the widget until you allow its host for scripts and for its requests:
kit: {
csp: {
directives: {
'script-src': ['self', 'https://backend.flawsnap.com'],
'connect-src': ['self', 'https://backend.flawsnap.com'],
},
},
},Merge these with your existing directives rather than replacing them.
Troubleshooting
[flawsnap] widget disabled:onvite preview: the preview server uses a different port fromvite dev. Allow both.- The button appears on prerendered pages but not on others: check every page is rendered from
src/app.html, not a custom template. - A Refused to load the script error: the CSP is missing
https://backend.flawsnap.cominscript-src. - Reports are missing your API calls from the first load: the widget must start before your app makes them, which is why it sits in
app.htmlrather than in a component that mounts later.
Questions
- Does Flawsnap work with a static SvelteKit build (adapter-static)?
- Yes. Prerendered pages still come from
app.html, so the tag is in each one. Allow the domain you deploy to. - Do I need a Svelte package?
- No. The widget is a hosted script and works the same in any framework.
Try it on your own site
7-day free trial, then $9 a month for Starter or $19 for Pro, per organization, with unlimited team members.
Related
Updated