cookie consent for SvelteKit

Cookie consent for SvelteKit,
one server hook

cookiecrumbsHandle inserts the tag as the first element of every HTML response’s head and sets event.locals.consent for your load functions.

What changes in a SvelteKit page head once the runtime is first: nothing non-essential runs until its category is granted.

the rule

What the law asks of a SvelteKit site

A SvelteKit app loads its trackers from +layout.svelte on the client. No consent step, no record.

The rule is the same everywhere in the EU and the UK: nothing beyond what is strictly necessary may be stored on or read from a visitor’s device before they have agreed. That is Article 5(3) of the ePrivacy Directive, and the agreement has to meet the GDPR’s definition of consent: freely given, specific, informed and unambiguous, with a refusal that is as easy as the acceptance. The UK keeps the same rule in PECR, with narrow exemptions for low-risk statistics since 5 February 2026. California and the other US state laws work the other way round: no opt-in, but an opt-out link and support for the Global Privacy Control signal.

Three things follow for a website. Analytics, advertising and social scripts have to wait for a choice. The choice has to be recorded in a way you can show later, because Article 7(1) puts the burden of proof on you. And withdrawing has to be as easy as agreeing, which means a control that reopens the banner on every page. A banner that shows two buttons and loads Google Analytics anyway fails the first test, and that is the failure regulators have been fining, from the CNIL’s decisions against Google and SHEIN to the Dutch and Belgian authorities’ sweeps.

how it works

How consent works in a SvelteKit app

SvelteKit renders HTML through hooks.server.ts, and that is the cleanest place to put a consent runtime: a handle that transforms every HTML response and writes the tag first in the head, whatever the page or layout does. Compose it with your other hooks through sequence(); put %cookiecrumbs.head% in app.html if you want to choose the exact position yourself.

The same hook sets event.locals.consent, so a +page.server.ts load function can decide server-side whether to include an embed or its placeholder. On the client, consent stores keep every component in step: $consent for the categories, $regime for the applicable rule, and actions to open, accept, reject or withdraw.

SvelteKit’s onMount is where trackers tend to start. Wrap the subtree in ConsentGate, or check the store before initialising. Anything the scan finds on the deployed domain that is not tagged is handled by the block map, so a forgotten script in a legacy layout still waits.

set-up

Set it up in SvelteKit

Five steps. The CLI does most of them, and every one is also a click in the dashboard.

  1. Init from the repo

    Detects SvelteKit, links the site, writes the config.

    npx cookiecrumbs login
    npx cookiecrumbs init
  2. Add the hook

    Compose it with your other hooks through sequence(). Put %cookiecrumbs.head% in src/app.html to pick the exact position yourself.

    npm i @cookiecrumbs/sveltekit
    
    // src/hooks.server.ts
    import { cookiecrumbsHandle } from '@cookiecrumbs/sveltekit/server';
    
    export const handle = cookiecrumbsHandle({ site: 'pk_live_…', env: 'production' });
  3. Gate in the tree

    ConsentGate, CookieDeclaration and ManageCookiesLink Svelte components and consent stores ship with the package.

    <script>
      import { ConsentGate } from '@cookiecrumbs/sveltekit';
    </script>
    
    <ConsentGate category="marketing">
      <MetaPixel />
    </ConsentGate>
  4. Decide on the server when you can

    A load function can read the consent and skip an embed for visitors who said no, before any client code runs.

    // +page.server.ts
    export const load = ({ locals }) => ({
      showMap: locals.consent?.isGranted('marketing') ?? false,
    });
  5. Scan, sort, publish

    Scan, sort once per vendor, publish.

    npx cookiecrumbs scan --wait
    npx cookiecrumbs push --env production

watch out

Mistakes we see in SvelteKit apps

Each one shows up in the first scan as a tracker that fired before consent.

  • Adding the tag in +layout.svelteThat is client-side and after hydration. The hook writes it into the HTML response, first.
  • Prerendered pages missing the hookPrerendering runs through the same handle, so the tag is in the static HTML too. If you bypass the hook with a custom app.html, use the placeholder.
  • Form actions that set analytics cookiesServer-set cookies are cookies. Keep them necessary or gate them.
  • Multiple adapters, one keyProduction and preview have different keys. Use the preview key on staging.
  • No withdraw pathManageCookiesLink in the footer takes two lines.

before you ship

The compliance checklist

Eight lines. The dashboard ticks the last two from real data.

  • The tag is the first script in the headAnything above it can set a cookie the runtime cannot take back.
  • Every analytics, ads and social script waits for its categoryEither type="text/plain" with a category, or the host is in the block map from the scan.
  • Reject all is on the first layerSame size, same weight as Accept all. No “X” that counts as consent, no pre-ticked boxes.
  • Consent Mode defaults are denied before Google loadsCheck the seven signals in Tag Assistant with consent denied.
  • The imprint and privacy policy links are setPublishing is refused until both are there; the banner footer links to them.
  • A withdraw control is reachable on every pageThe persistent control or a “Manage cookies” link in your footer.
  • The first full scan ran and every vendor is filedThe verdict on Home says what fired before consent, dated.
  • The install check passedIt loads your page like a visitor and confirms the banner ran before any tracker.

what you get

What you get on the free plan

Two domains, 100 subpages scanned a month per domain, every regulation, the CLI, the SDKs and the MCP server.

A scanner that reads the rendered page

Every page, in each consent state, in a real browser. Cookies, pixels, iframes and storage keys, with the page and script that set them. Re-scanned on a schedule.

The cookie declaration writes itself from the result.

A banner that blocks first

Tags stay inert until their category is granted. Reject all is as visible as Accept all. Seven layouts, 25 languages, Consent Mode v2, and the right rule per region.

21 kB, one file, no third-party requests of its own.

Proof you can hand over

Each choice is stored with a timestamp, the banner version and a hash of the texts shown, chained to the record before it. Exports are signed. Hosted in Frankfurt.

Article 7(1), on the free plan.

free check

Check what your site loads right now

The homepage HTML, matched against the tracker database. No account, nothing stored. The full scan in the dashboard does the same for every page, in a real browser.

questions

Straight answers

Can I read the consent on the server?
Yes. The hook sets event.locals.consent from the consent cookie, so a load function can decide what to render before the client runs.
Does it work with adapters for Vercel, Netlify and Node?
The hook runs inside SvelteKit’s request handling, so it works with every adapter that serves HTML responses, and with prerendering.
Which components and stores are included?
ConsentGate, CookieDeclaration and ManageCookiesLink, plus stores for the consent state, the regime and the actions.
What about Google Tag Manager?
Keep it. The runtime sets Consent Mode v2 defaults first and the GTM template adds the triggers; or give the GTM tag a category.
Does it affect performance?
The runtime is 21 kB gzipped, async, no third-party requests of its own, and the bar and ribbon layouts reserve space before paint.
Can I position the tag myself?
Yes. Put %cookiecrumbs.head% in src/app.html and the hook fills it there instead of the top of the head.

next step

Ship the banner with the next deploy

Add the site, run npx cookiecrumbs init in your SvelteKit repo, and the consent log starts recording in the EU.