Cookie consent for SvelteKit

The tag goes in src/app.html, before %sveltekit.head%, so it is first in <head> on every page, server-rendered or not. SvelteKit reads the key from a public environment variable at render time.

Set it up with your AI agent

Connect the Croombit MCP server to your editor or coding agent and paste this. It creates the site, the cookie categories and the banner, and edits your code.

Using Croombit: create a site for this project and keep the cookie categories it comes with. Write the banner text and publish the banner. Create a TEST publishable key for localhost:3000. This is a SvelteKit app: add cmp.js to src/app.html before %sveltekit.head%, with the key from %sveltekit.env.PUBLIC_CROOMBIT_KEY%, and only start our analytics SDK after the croombit:consent event grants ANALYTICS.

The agent works in TEST. Going live takes two steps in the console: verify your domain and swap in a LIVE key — see going live.

Or by hand

1. Add the tag to app.html

Put the key in .env as PUBLIC_CROOMBIT_KEY. %sveltekit.env.PUBLIC_…% is replaced when the page renders.

<!-- src/app.html -->
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <script src="https://croombit.com/cmp.js"
            data-tc-api-key="%sveltekit.env.PUBLIC_CROOMBIT_KEY%"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    %sveltekit.head%
  </head>
  <body data-sveltekit-preload-data="hover">
    <div style="display: contents">%sveltekit.body%</div>
  </body>
</html>

2. Hold back trackers

Tags in app.html or <svelte:head>: type="text/plain" plus data-tc-consent. Tags your code inserts at runtime from well-known hosts are held back automatically.

<script type="text/plain" data-tc-consent="analytics" async
        src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
<script type="text/plain" data-tc-consent="analytics">
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXX');
</script>

An analytics or session-replay SDK you import isn't a script tag, so start it when its category is granted:

<!-- src/routes/+layout.svelte -->
<script>
  import { onMount } from 'svelte';

  // Start an SDK you install from npm only once its category is granted.
  onMount(() => {
    const on = (e) => {
      if (e.detail?.categories?.ANALYTICS) startAnalytics();
    };
    window.addEventListener('croombit:consent', on);
    return () => window.removeEventListener('croombit:consent', on);
  });
</script>

<slot />
<a href="#" data-tc-open-preferences>Cookie settings</a>

Questions

Does client-side navigation show the banner again?

No. The decision is stored once and applies to every route; SvelteKit never replaces the element the banner lives in.

Does it work with adapter-static?

Yes. With prerendering, %sveltekit.env.PUBLIC_…% is filled in at build time, so build with the key you deploy.