In c15t 3.2, consent no longer makes your scripts late. Scripts that a visitor already allowed now start before the page hydrates. This release also adds a shadcn registry, 18 languages, and integrations for HubSpot, FullStory, Amazon Ads, and more.
Fast script loading
When a visitor already gave consent, c15t starts their allowed scripts from the server HTML, before the page hydrates. This works in Next.js, TanStack Start, Nuxt, and SvelteKit. See Loading scripts from the server for the benchmarks and implementation details.
Vendor helpers in @c15t/integrations that support early starts run before
hydration. Other helpers load after hydration. Nothing starts early while the
policy asks the visitor to choose, or when the page comes from prerendered or
cached HTML. To turn the early start off, set scriptLoader: { serverStart: false }. See Start allowed scripts before
hydration.
Browser manifest mode also does less work. With manifest({ resolve: 'browser' }), c15t skips the /manifest request when the server state already holds the
visitor's policy. It also skips the policy resolver, which is about 20 KB
gzipped.
shadcn registry
Your consent banner can now use the same components as the rest of your app. Add it with the shadcn CLI:
Each item renders your project's own shadcn components, and the markup is yours
to change. c15t keeps the consent logic, so you still get its fixes when you
update c15t.
Follow the Next.js shadcn guide to connect your Inth project and add the components to your app.
| Item | What it gives you |
|---|---|
consent-manager | The provider setup for your framework |
consent-banner | The banner, also as a consent wall that blocks the page |
consent-dialog, consent-sheet | The preference center in a dialog or a sheet |
consent-preferences | The category and vendor switches |
consent-dialog-trigger | A button that floats on the page and opens the preference center again |
consent-widget | The preference center inline on a page |
consent-gate | A gate that holds an embed until the visitor allows its category |
consent-branding | The "Secured by" link and the trigger mark |
Each item has a next-, react-, and tanstack-start- version. The registry
has a build for all 27 shadcn styles, so Radix, Base UI, and React Aria projects
all work. It has no IAB TCF items yet. Under an IAB policy, the TCF banner from
<IABProvider> shows instead of the shadcn banner.
Build your own banner
The registry items use public headless hooks, so a banner in any design system
can use them too. useBannerCopy() and useConsentLabels() return the same
copy and button labels as the stock banner, in the active language.
useConsentGate(), useScrollLock(), useConsentTextDirection(), and
StreamedConsentSurface cover the rest of what the stock UI does. See headless
UI.
Vue, Svelte, and the script tag get the same copy as plain functions:
resolvePromptCopy, resolveActionLabels, and
resolvePreferenceControlLabels.
18 new languages
More of your visitors can now read the banner in their own language. c15t ships consent copy in 53 languages, up from 35 in 3.1.
- Arabic (
ar), Urdu (ur), and Yiddish (yi), which read right to left - Bangla (
bn), Haitian Creole (ht), Korean (ko), and Russian (ru) - Japanese (
ja), Thai (th), Malay (ms), Vietnamese (vi), and Turkish (tr) - Tamil (
ta), Telugu (te), Marathi (mr), Kannada (kn), Malayalam (ml), and Punjabi in Gurmukhi script (pa)
With these, c15t covers all 12 languages that New York State's language access law names for state agencies.
manifest() loads each language on demand, and the backend serves them from
/init. To bundle them, import one with @c15t/translations/<code> or all of
them with @c15t/translations/all. See
translations.
Banners, dialogs, and preference centers also read right to left for Central
Kurdish (ckb), Uyghur (ug), Syriac (syr), and N'Ko (nqo). A script
subtag now sets the direction, so ku-Arab reads right to left.
Integrations
New consent-gated helpers in @c15t/integrations cover:
- Analytics and visitor identification: FullStory, Gainsight PX, Lucky Orange, Pendo, Statable, RB2B, HubSpot, Default, and Clay.
- Experiments and personalization: Adobe Target, Appcues, Dynamic Yield, Optimizely Web Experimentation, and Wisepops.
- Email and CRM: ActiveCampaign, Attentive, Customer.io, Insider, Marketo Munchkin, Oracle Eloqua, and Salesforce Account Engagement.
- Ads and pixels: AdRoll, Amazon Ads, LiveIntent, The Trade Desk, and Vector.
- Chat and support: Ada, Gorgias Chat, and Zendesk Messaging.
Default has separate Pixel and Forms SDK helpers. Choose one per page.
Databuddy consent and session replay
Databuddy now waits for measurement consent and supports optional session replay and tracking feature switches. See the Databuddy guide.
Shopify and Google Tag Manager
- Shopify.
createShopifyCustomerPrivacysends each c15t choice to Shopify's Customer Privacy API. Hydrogen holds analytics events until the visitor answers the c15t banner. See the Shopify guide. - Google Tag Manager. A community template loads the c15t script tag from GTM and sends each choice to Consent Mode. See the Google Tag Manager guide.
A subject ID from the first page load
snapshot.subject.subjectId now exists before the visitor makes a choice.
The first save keeps that ID, and clearing choices or revoking consent keeps
it too. The ID alone grants no consent, so the banner still shows.
Next.js, TanStack Start, Nuxt, Astro, and SvelteKit can set the c15t-sid
cookie on the first request, so the server and browser start with the same
ID. Other servers can use resolveRequestSubject() from c15t/server.
See subject IDs.
identify() now works before the first consent save. The backend creates the
subject and links the identity in one transaction. Acknowledging a notice that
offers only necessary categories also keeps the visitor's ID across reloads.
Keep fixed elements still while scrolling is locked
When a blocking banner or preference dialog needs scrollbar padding, c15t now
exposes that width as --c15t-scroll-lock-gap. Use it in the right offset or
padding of fixed headers and chat buttons to keep them in place. The padding
also accounts for CSS zoom. See
scroll lock styling.
Svelte and SvelteKit through c15t
Install c15t and import from c15t/svelte, as you do for the other
frameworks. SvelteKit helpers live at c15t/svelte/kit, server resolution at
c15t/svelte/server, and the Vite plugin at c15t/svelte/vite. c15t setup
writes these imports for you. See the SvelteKit
quickstart.
offline() is now local()
We renamed offline() to local() to make clear that choices stay in the
browser. Development builds now show the limits once per page:
offline() still works until v4. See local mode.
Clearer backend errors
When the backend refuses a request, the error now says why. In 3.1, an error
over HTTP/2 could stop at /init responded 402. Now it includes the backend's
message and code, with status and code properties to match.
After a 402 response, c15t no longer sends the consent save again. The choice stays in the browser.
Consent only from sites you trust
The backend now records consent only for sites in trustedOrigins. A save
naming any other site gets 400 DOMAIN_NOT_ALLOWED and writes nothing. In
3.1, any site could create a row in the domain table. Only an entry's host
counts, so localhost:3000 covers localhost, and * still covers every
site. Without trustedOrigins, saves are not checked.
identityToken.requireVerifiedLinks is a new option, off by default. It
stores a link to your user ID only when an API key or identity token proves
it. Without proof, identify() gets 401 IDENTITY_TOKEN_REQUIRED, and a
consent save is recorded without the user ID.
See origins, identity and secrets and verify identity links.
Fixes
- A preference center that a visitor opens before the backend answers now stays open when the answer arrives.
- Under an IAB policy, c15t now creates the IAB CMP once per page load. In 3.1, it could create the CMP up to 100 times.
- The backend now records the tenant on
identify_useraudit entries. - The script-tag banner and DevTools panel stay visible on pages whose CSS hides empty elements, including Shopify's Dawn theme.
- The c15t tab now supports Nuxt DevTools 4, including popup windows. See Nuxt DevTools.
- The dialog close-button offset and small and large trigger labels now scale with the visitor's text-size setting.
- The backend now accepts consent timestamps up to 10 minutes ahead of its clock and stores them unchanged. A slightly fast visitor clock no longer causes a consent save to fail.
- Invalid identity links return a validation error before the backend writes anything. Retrying an unchanged link no longer adds duplicate audit entries.
Before you upgrade
Update c15t and any @c15t/* packages you use to 3.2 together.
If you set trustedOrigins on a self-hosted backend, make sure it lists every
site that saves consent, including the site the backend is mounted on. In 3.1,
a same-origin save worked without it. In 3.2, a save from a site not on the
list gets 400 DOMAIN_NOT_ALLOWED. c15t keeps a refused save and retries it
up to 10 times over 7 days, so a site you add in that time still gets its
consent recorded.
If your cookie policy lists c15t cookies, add c15t-sid and
c15t-notice-subject. Their prefixes follow your custom storageKey.
If you use the Next.js proxy or SvelteKit handle, set subjectCookie: false
for routes whose responses enter a shared cache. Also disable server subject
cookies when browser persistence is off or a consentSource owns consent.
Pass the same storage settings to the server helper and browser. See the
Next.js subject cookie setup
and SvelteKit request setup.
If your own code calls POST /subjects or PATCH /subjects/:id, send
Content-Type: application/json. Both routes now answer other body types with
415 UNSUPPORTED_MEDIA_TYPE, as 2.x did. A save's domain must also be 1 to
253 characters with no control characters. The c15t clients and
@c15t/node-sdk already meet both rules.
Two changes can move things on a page:
- The banner now stays in its corner for right-to-left languages. In 3.1, a
bottom-leftbanner moved to the bottom right for Hebrew. To put the banner on the right for right-to-left pages, setposition. - Plain
kunow reads left to right, because it usually means Kurmanji. For Sorani, useckborku-Arab.