# Framework integration Authoritative basis: Next.js documentation for `next/script` and `@next/third-parties`; Nuxt Scripts documentation for `useScriptTriggerConsent`; SvelteKit documentation for `csp` config and the `handle` hook. Status as at 2026-08-05. Verify against the installed version — these APIs move. ## The SSR trap On the server, consent state is unknown unless it arrives in the request. That produces three failure modes, in ascending order of how long they take to find: 1. **Rendering the banner into the HTML for a user who already decided.** Cosmetic flicker, annoying, harmless. 2. **Rendering the tag into the HTML because a build-time flag said analytics is on.** The tag is in the served markup and fires before any client code runs. This is the failure the whole skill exists to prevent. 3. **A CDN caching a page rendered for a consenting user and serving it to everyone.** The gate is bypassed for every subsequent visitor and the network trace looks clean in the developer's own browser, because their consent is stored. Rules: - **Never let server-rendered output contain an unblocked third-party tag.** The tag is injected client-side, always, or shipped as `type="text/plain"` for the unblocker. - **Read state from the request or not at all.** A cookie is readable server-side; `localStorage` is not. If you need server-side awareness, put the decision in a first-party cookie as well as in `localStorage`. - **Vary or do not cache.** If any part of the HTML depends on consent, either add `Vary` on the consent cookie or move that part to the client. - **Render the banner client-side after hydration**, or render it server-side only when the request carries no consent cookie. ## Next.js App Router `next/script` strategies, from the documentation: | Strategy | Behaviour | Use for consent work | |---|---|---| | `beforeInteractive` | Injected into the initial HTML from the server, downloaded before any Next.js module, executed in order before hydration; must be placed in the root layout; always injected into `
` | The consent manager itself and the Consent Mode `default` call — the documentation names cookie consent managers as an intended case | | `afterInteractive` | Default; loads client-side after some hydration | Tags that are already gated by your own logic | | `lazyOnload` | Loads during browser idle time after page load | Low-priority gated widgets | | `worker` | Partytown web worker; **not supported in the app directory** | Not usable here | `onLoad`, `onReady` and `onError` do not work in Server Components. Any component using them must carry `'use client'`. Correct ordering in `app/layout.tsx`: ```tsx import Script from 'next/script'; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {/* 1. Consent Mode defaults — inline, executes first */} {children} {/* 2. Banner is a client component; it injects tags itself after the choice */}