c15t v3 is a rebuild. v2 was a React library with a Next.js adapter. v3 is one consent engine with adapters for eight frameworks and a script tag, and every adapter passes the same conformance suite, so a policy behaves the same in Nuxt as it does in Next.js. Server-rendered apps can now decide consent on the server and send the banner with the page.
Your visitors shouldn't notice the upgrade, because v3 reads the consent v2 stored. Your code will notice. Read Upgrading before you start.
Highlights
- Eight frameworks and a script tag. TanStack Start, Nuxt, Vue, Astro,
Svelte and SvelteKit join Next.js and React.
@c15t/browsercovers plain HTML, WordPress, Shopify and other CMS sites. - One package.
npm install c15t, then import fromc15t/next,c15t/react,c15t/vue,c15t/tanstack-startorc15t/astro. - Banner in the first HTML. Server-rendered frameworks can render the banner on the server. v2 always mounted it after hydration. On cookiebench, the c15t banner showed in 27 ms on desktop. The median of the 10 tested tools was 471 ms.
- Consent manifest. Your server decides each visitor's policy from a manifest that your build downloads or your server caches. With 150 ms of backend latency, time to first byte in our Next.js benchmark fell from 157.6 ms to 7.4 ms.
- Less work in the browser. Smaller render-blocking CSS, themes built on the server, the dialog and optional modules loaded on demand, and React components that re-render only when the value they read changes.
- Policy rules. A simpler policy format, 34 presets instead of 6, notice-only prompts, and Global Privacy Control applied live instead of stored as a refusal.
- Per-vendor consent. Visitors can switch off one vendor inside a category they allowed, without IAB.
- GPP and publisher restrictions. GPP 1.1 with 16 US state sections, and IAB publisher restrictions encoded in the TC string.
- New integrations. Cloudflare Zaraz, Klaviyo, Sentry, Vercel Speed
Insights and the OpenAI pixel for ChatGPT Ads.
@c15t/scriptsis now@c15t/integrations. - Rewritten self-hosted backend. Built on Effect SQL, with PostgreSQL, MySQL and SQLite, and a migrator that adopts your v2 database.
One engine, more frameworks
v2 kept consent in a Zustand store that set up blockers and wrote to window
as soon as you created it. v3's consent kernel does nothing until you call a
command, so it is safe to import on the server, and every adapter reads it
the same way. One conformance suite tests React, Vue and Svelte against the
same policy, storage, event and accessibility checks, including records
written by v2.
In v2, the c15t package was the headless engine. In v3 it installs the
engine and the adapters, and the root c15t import is still the engine. The
scoped packages, such as @c15t/nextjs, are still published. Svelte, the
script tag and the vendor helpers install separately as @c15t/svelte,
@c15t/browser and @c15t/integrations.
| Framework | What's new | Start here |
|---|---|---|
| Next.js | Server-side consent in both the App Router and, new in v3, the Pages Router. Works with static export, ISR and Cache Components. withConsentManifest() reads your c15t.config.ts, so you write no client wrapper. Needs Next.js 15 or 16. | Quickstart |
| TanStack Start | New adapter. Consent resolves in the root route loader. | Quickstart |
| React | Client-rendered apps such as Vite, React Router and Remix. Needs React 18 or 19. | Quickstart |
| Nuxt | New module, with server rendering and a Nuxt DevTools tab. Works with Nuxt 3 and 4, including Vue Vapor pages in Nuxt 4.6. | Quickstart |
| Vue | New plugin for Vue 3 apps without Nuxt. | Quickstart |
| Astro | New integration. The banner is plain .astro markup with no framework JavaScript. The dialog is an island in Svelte, React or Vue. | Quickstart |
| Svelte and SvelteKit | New package, @c15t/svelte, with SvelteKit helpers in @c15t/svelte/kit. | Svelte, SvelteKit |
| HTML and CMS sites | New package, @c15t/browser, with setup steps for WordPress, Webflow, Shopify and seven other platforms. | Quickstart |
| JavaScript | createConsentRuntime() for your own UI, or @c15t/browser for the stock banner. | Quickstart |
The script tag needs no build step. Your Inth project serves it with your backend URL and policy already inside:
A self-hosted backend serves the same file at its own /c15t.js. jsDelivr
serves it too, with a data-backend-url attribute. Sites without a backend
use c15t.offline.js.
Faster pages
Most of the speed in v3 comes from deciding consent earlier, on the server where possible, and from keeping CSS and optional code off the critical path.
The banner in the first HTML
Next.js, TanStack Start, Nuxt, Astro and SvelteKit can resolve consent while
they render, so the banner arrives with the page and a returning visitor's
gated scripts start at hydration. A slow backend can't hold the page past a
500 ms budget by default. The Next.js App Router streams consent by default.
The page renders without a wait. The banner follows in a later chunk of the
same response, before hydration. Set streamBanner: false to mount it
after hydration. Next.js rendering compares
the options.
Banner visibility on cookiebench
Cookiebench measures when a cookie banner becomes visible on a Next.js page. It compares 10 consent tools and records the median of 15 visits per tool. Inth, the team behind c15t, maintains cookiebench, and its methodology is public.
| Profile | c15t | Next fastest tool | Median of 10 tools |
|---|---|---|---|
| Desktop | 27 ms | 385 ms | 471 ms |
| Mobile, 4× CPU slowdown, 200 KB/s | 240 ms | 2.15 s | 2.71 s |
c15t showed its banner first on both profiles, and it added 0 ms of Total Blocking Time. These results come from cookiebench v3.2.4 and c15t 3.0.0-alpha.11. The c15t run used a different machine from the other 9 tools.
Consent manifest
The backend has a new GET /manifest endpoint. It returns one public document
per project with everything /init needs and no visitor data, so a CDN can
cache it. Your server resolves each visitor from this manifest and the
request's country, region, language and GPC headers. Page renders stop
waiting on the consent backend.
The recommended setup downloads the manifest when you build the app. Build
plugins for Next.js, TanStack Start, Astro, Nuxt and Vite fetch it from your
backend. The snapshot stays fixed until the next build, and a failed fetch
stops the build. For policy changes that must apply without a rebuild, your
server can fetch and cache the manifest at runtime instead. If the server
can't read it, the server falls back to /init.
In a Next.js 16 production build with a local backend that adds 150 ms to each request, medians of 10 runs:
| Server render | Time to first byte | Banner painted |
|---|---|---|
Backend /init on every request | 157.6 ms | 180 ms |
| Manifest, warm cache | 7.4 ms | 28 ms |
Both rows are v3. v2 never put the banner in the server HTML, so it never paid for that round trip. Data fetching compares the modes, and each framework's rendering guide shows how to turn the manifest on.
Less work in the browser
- CSS. The main stylesheet holds only what a first paint can show, and dialog styles load with the dialog. In a Next.js 16 build, render-blocking CSS from c15t fell from 10.3 KB to 8.8 KB gzip.
- Themes.
ConsentThemeandgenerateThemeCSS()render theme CSS on the server. v2 sent every visitor about 1.9 KB gzip of generator code. - React re-renders. Components re-render only when the value they read changes. With 10 components reading 5 categories, changing one category caused 20 renders in v2 and 2 in v3.
- On-demand code. The script loader, network and iframe blockers, IAB, GPP and the preference dialog load only on pages that use them.
- Translations. Each language has its own entry, such as
@c15t/translations/de. A page loads only the languages it shows. - IAB pages. Server integrations send a reference to the Global Vendor List instead of the list. With 1,211 vendors, the init JSON drops from 860 KB to 6.6 KB.
Policies
Policy packs are now policy rules. A rule describes behavior only: who sees a
prompt, what kind, and which categories it covers. Layout moves to the
client's presentation option. Rules with unknown keys or categories now fail
validation.
- Presets.
policyPackPresetsis nowpolicyRulePresets, andworldNoBanner()is nowworldOptOutNoPrompt(). 28 presets are new, covering the US privacy states, Canada, Australia, Japan, Switzerland, the UK, Brazil, India and much of Asia and the Middle East. Each lists its sources inreview. A preset is a starting point, not legal advice.recommendedPolicyRules()gives you a ready set. Thepolicy-packs-to-policy-rulescodemod renames both. - Notice prompts.
prompt: 'notice'asks for an acknowledgement instead of a choice, and leaves earlier refusals in place. - No jurisdiction label. v2 picked
GDPR,CCPAorNONEfrom a fixed country table. v3 decides from your rules alone. - Consent records. A record stores only what the visitor did. c15t works out permissions against the current rule each time it reads it, so the same record can mean different things under opt-in and opt-out rules.
- Asking again. c15t asks again when a choice expires. It also asks again
when a change to the rule affects that choice. Bump
copyRevisionto ask everyone after a wording change. - Global Privacy Control. c15t reads GPC live on every evaluation and never stores it as a refusal. The restriction ends when the browser stops sending it.
Policies and how consent works cover the model.
Consent controls
- Per-vendor consent outside IAB. Declare
vendors, and the preference center lists each category's vendors with its own switch. Integration helpers declare their own vendor. ConsentGatereplacesFramein every framework. It holds back an embed until the visitor allows its category or vendor.- Banner presentation.
presentationpicks afloating,bar,widgetorwallbanner, its position and its actions. Clicks on the empty space around a non-blocking banner reach the page below it. - Experiments. A/B test presentation and theme, with a per-arm summary from the backend. See banner experiments.
- Clear on revocation.
clearOnRevocationdeletes declared cookies and storage keys when a visitor revokes a category. - Follow another CMP.
consentSourcemirrors an existing CMP's decisions.
IAB TCF and GPP
- Global Privacy Platform. c15t can install the GPP 1.1 API at
__gpp. IAB rules add the TC string, and US rules add the visitor's state section for 16 states, with the US National section for everyone else. - Publisher restrictions. v2 never encoded them. v3 writes them into the TC string, applies them to gated scripts and shows each vendor under the legal basis they leave.
- IAB in every framework. v2 had IAB banners for React and Next.js only. v3 adds Nuxt, Vue, Astro, Svelte, SvelteKit and the script tag.
TCF 2.4 support arrived in v2.3.0 and carries over.
Integrations
@c15t/scripts is now @c15t/integrations. Subpaths and helper names stay the
same. All 37 v2 helpers carry over, and 5 are new, for 42 in total.
@c15t/scripts stays published as a deprecated re-export until v4.
| New integration | What it does |
|---|---|
| Cloudflare Zaraz | Maps c15t categories to Zaraz purposes. Zaraz still runs the tools. |
| Klaviyo | Signup forms and onsite tracking, with a forms-only mode that keeps tracking off. |
| Sentry | Error reports without user data until the visitor allows measurement. Session Replay waits for that consent too. |
| Vercel Speed Insights | Loads the Web Vitals collector after measurement consent and drops its reports after a withdrawal. |
| OpenAI pixel | The ChatGPT Ads measurement pixel, with typed conversion events. |
createEventDispatcher() sends one event to every allowed integration that
has an event API. Each helper now lists its vendor in the preference dialog,
with its own switch.
- Google.
gtag()andgoogleTagManager()takeloadMode: 'after-consent'. With it, the browser sends Google nothing until the visitor allows the category. - PostHog. The
featuresoption turns off surveys, heatmaps, dead clicks, Web Vitals and feature flags. A module that you turn off never loads. - Mixpanel. Visitors start opted out. Before this change, Mixpanel could store events and a visitor ID before the first choice.
- Fixes. Helpers re-run their consent steps only when their own vendor's consent changes. A helper without its vendor ID logs a warning and loads nothing. Matomo, Amplitude, Heap, Umami, Meta, X, TikTok and Crisp have fixes.
Segment takes new options too. See the integrations overview.
Self-hosted backend
@c15t/backend is rewritten on Effect 4 and Effect SQL, and
c15tInstance(options).handler(request) works as before. Queries use joins
and new indexes. In our benchmark, with PGlite and 1,000 subjects among 20,000
other rows, the subject read path went from 11.9 ms to 3.3 ms median.
- PostgreSQL, MySQL and SQLite replace the Drizzle, Prisma, TypeORM, Kysely and MongoDB adapters. MongoDB has no migration path.
c15t self-host migrateadopts a v2 schema without dropping tables or columns.- New routes serve the manifest, session reports for visitor counts, experiment summaries and the script-tag bundles.
- The backend accepts a save retried up to 7 days after a failure, if the policy did not change since the click.
- Lookups by your own user ID count only verified identity links. An API key
or a signed
identityTokenverifies a link. Links from v2 stay unverified until you link them again. - A v3 backend can share a database with a v2 backend while you roll out.
See the backend quickstart and database setup.
@c15t/node-sdk has a new client, createC15tClient(). Methods return a
result instead of throwing, and transient failures retry. See the
Node.js SDK reference.
CLI and dev tools
c15t setupscaffolds every supported framework, and--planpreviews the changes first. It writes the same files that the quickstarts show. See setup.c15t setup --codexgives a coding agent the rules to install c15t, upgrade from v2 or replace another banner. The agent then checks first visit, reject, accept and withdrawal in a browser.- Codemods migrate most v2 source: the
provider and its transports, moved exports,
useConsentManager(), callbacks, policy presets, CSS variables, the Tailwind CSS 3 plugin, dev tools,@c15t/scriptsimports, the Node.js SDK and the backend config. Where a change needs a decision, they leave aTODO(c15t v3)comment that fails the build. - Dev tools no longer depend on React. There is a panel for any framework, plus TanStack Devtools and Nuxt DevTools tabs. The consent trigger shows a DevTools button, so the two launchers no longer overlap.
Upgrading
Each v2 package has a step-by-step guide that opens with a prompt you can paste into a coding agent: Next.js, React and JavaScript. If you run your own c15t backend, upgrade it and the Node.js SDK in the same release.
Visitors keep their choices. v3 reads the c15t cookie and storage key
v2 wrote. A v2 denial keeps blocking its category, and a v2 grant keeps
applying until it expires. When the rule comes from a preset and the v2 record
noted its policy, v3 also asks again if that policy changed.
Upgrade clients and backend together. A v3 client can't read a v2
backend's /init. It shows no banner and keeps optional categories denied.
Inth-hosted URLs work with v3 clients.
Start with the codemods.
Review the proposed files, then run the command again without --dry-run.
Self-hosted backends and Node.js servers add backend-config-to-v3 and
node-sdk-to-v3.
Breaking changes
The upgrade guides cover each of these with before and after code.
- Every package. ESM only. React 18 or later and Next.js 15 or later.
- Provider.
ConsentManagerProvideris nowConsentProvider, andmodeandbackendURLmerge into one value, such ashosted({ backendURL }). Next.js server rendering moves fromfetchInitialData()toresolveConsent()andConsentRoot. Codemod:consent-provider-options. - Hooks.
useConsentManager()is gone, replaced by one hook per field. Codemod:use-consent-manager-to-hooks. - Exports. Headless hooks, trigger atoms, token types and flat banner
parts leave the
c15t/reactandc15t/nextroots for subpaths. Codemod:root-exports-to-subpaths. - Callbacks.
onConsentSetandonConsentChangedbecomeonPermissionsChangedandonChoiceRecorded, with new payloads. Codemod:callbacks-to-v3. - Themes and styles.
themetokens no longer produce CSS on their own, so renderConsentTheme. Tailwind CSS 3 needs thec15t/postcss-tailwind3plugin, and v3 renames several CSS variables. Codemods:theme-to-consent-theme,postcss-tailwind3andcss-variables-to-v3. - IAB. The
iabprovider option becomesIABProvider, and@c15t/iabdropscreateIABManager. Codemod:iab-option-to-iab-provider. - Removed.
YouTubeEmbed,GoogleMap,useConsentScript()andConsentButton. - JavaScript. The v2 store and
getOrCreateConsentRuntime()are gone. - Self-hosted backend.
adapterbecomesdatabase,policyPacksmoves tomanifest.policyRules, and the schema needs a migration. Codemod:backend-config-to-v3. - Node SDK.
c15tClient()becomescreateC15tClient(), andconsents.checkneeds a client with an API key. Codemod:node-sdk-to-v3. - CLI and dev tools. Run
c15t loginagain. Dev tools move toc15t/react/devtoolsandc15t/next/devtools. Codemod:dev-tools-to-c15t.
Thank you to our contributors
, , , ,