Skip to main content

Event Dispatchers, Google Additional Consent and TanStack Start 1.0

Minor releaseNo breaking changes

Minor release adding an event dispatcher to every framework, Google Additional Consent for IAB TCF, and TanStack Start 1.0 support, with fixes for vendor switches, dialog scrolling, legal links, PostHog and the CLI.

c15t 3.1 gives every framework one event dispatcher for your own analytics calls, adds Google Additional Consent to IAB TCF setups and supports TanStack Start 1.0. It also fixes problems found since 3.0.0. The dialog showed vendor switches nobody asked for, stopped scrolling under some app CSS, and ran legal links into the banner text.

If you self-host the backend, run its migrations before you deploy 3.1. The 3.1 backend reads and writes a new column on every consent save, so it can't record consent until migration 9 has run. Also read Before you upgrade if you show vendor switches, use @c15t/iab with custom translations, or type your __tcfapi callbacks.

Highlights

  • Event dispatcher in every framework. useEventDispatcher() and getEventDispatcher() send your own events and pageviews to the scripts you registered, and drop them when the visitor hasn't consented.
  • Google Additional Consent. IAB TCF setups can ask for consent for Google ad tech providers that are not on the Global Vendor List.
  • TanStack Start 1.0. @c15t/tanstack-start builds and tests against TanStack Start 1.0.
  • Vendor switches only when you declare vendors. Sites that add only @c15t/integrations helpers get category switches, as in v2.
  • Fixes. The preference dialog scrolls again, legal links read the same in every framework, and PostHog stays off after a refusal that arrives while its script is still downloading.

Send your own events

Each framework now gives you the provider's event dispatcher. It sends to the scripts registered with the provider and checks consent when the event goes out, not when you created the dispatcher.

FrameworkImport
React, Next.js, TanStack StartuseEventDispatcher() from c15t/react, c15t/next or c15t/tanstack-start
Vue and NuxtuseEventDispatcher() from c15t/vue/vue-plugin, auto-imported in Nuxt
Svelte and SvelteKitgetEventDispatcher() from @c15t/svelte
AstrogetEventDispatcher() from c15t/astro/client
Script taggetEventDispatcher(runtime) from @c15t/browser
import { useEventDispatcher } from 'c15t/react';

export const SearchButton = ({ resultCount }: { resultCount: number }) => {
	const events = useEventDispatcher();
	return (
		<button
			type="button"
			onClick={() => {
				events.track('search', { resultCount });
			}}
		>
			Search
		</button>
	);
};

All dispatchers for one provider share a pageview history. A consent change or a remount no longer drops or repeats a pageview, and an event sent right after the visitor withdraws consent is dropped. Pass scripts to add destinations and pageviews to choose which integrations get single-page-app pageviews. createEventDispatcher from @c15t/integrations/events still works for code outside a provider.

→ React · Next.js · Nuxt · Svelte · Astro · JavaScript

Google works with ad tech providers that are not on the IAB Global Vendor List. List their Google ATP IDs in the manifest's iab.additionalConsent.providers. The self-hosted backend loads their names and privacy policies, caches them and sends them in /init. Setups without a backend can pass additionalConsent.providers to the browser instead.

The React, Vue, Svelte and script-tag preference centres list each provider with its privacy policy and a switch, and the banner's partner count includes them. __tcfapi returns an Additional Consent v2 string, such as 2~89~dv.2572, as addtlConsent beside tcString. A provider counts as consented only when its switch is on and the visitor consented to TCF purposes 1, 3 and 4.

c15t stores the string in the addtl_consent cookie and localStorage entry, restores it for returning visitors and removes it with the TC String. The self-hosted backend records it in a new consent.addtlConsent column.

→ React IAB · Next.js IAB · Backend configuration

TanStack Start 1.0

@c15t/tanstack-start now builds and tests against TanStack Start 1.0 (@tanstack/react-start 1.169 and @tanstack/react-router 1.170.44). The peer ranges are unchanged, so later 1.168 releases still work.

TanStack Start 1.0 needs Vite 8. Upgrade Vite first, then upgrade @tanstack/react-router with @tanstack/react-start, because each Start release depends on one exact router version. Start 1.0 also renames .inputValidator() to .validator() on createServerFn() and createMiddleware(). c15t and the code its CLI generates don't call either.

Fixes

  • Vendor switches. Since 3.0.0, the preference dialog showed a switch for the vendor of each @c15t/integrations helper, so a site that added only posthog() got a PostHog switch under Analytics. The dialog now shows only category switches until you declare a vendor. A helper vendor the visitor already switched off stays listed, so they can turn it back on, and its saved denial keeps blocking it until they do.
  • Dialog scrolling. Visitors can scroll to every category and vendor again. overflow: hidden on the category list or a category row, for example to clip rounded corners, used to stop the list scrolling on phones.
  • Saves interrupted by navigation. When a visitor made a choice and then reloaded or left the page before the backend answered, the browser kept the choice but the backend never recorded it. c15t now queues each choice before sending it, and the next page load resends it with the original decision time.
  • Legal links. Astro, Vue, Nuxt, Svelte and the script tag now match React: a space after the description, then the links separated by commas.
  • Vendor list requests. In manifest mode, the browser fetches the Global Vendor List without the x-c15t-version and x-c15t-policy-contract headers. The request skips the CORS preflight, and hosts that reject those headers no longer fail it.
  • TC data outside TCF. When the visitor's policy doesn't use IAB TCF, __tcfapi returns only the base fields CMP API v2 allows. No TC String, addtlConsent, purpose or vendor data reaches vendors.
  • PostHog. With posthog({ loadMode: 'after-consent' }), a refusal that arrived while array.js was downloading let PostHog start once the download finished. The refusal now clears PostHog's queued init.
  • Sentry. sentry({ getClient }) now declares the same vendor name and privacy policy as sentry({ dsn }), so Sentry gets a vendor switch and core stops warning about a missing declaration.
  • CLI. Canary builds of the CLI install c15t@3 and other @c15t/* packages from @latest instead of @canary. Setup also replaces a c15t package pinned to an exact prerelease.
  • Svelte. @c15t/svelte declares svelte@^5.20.0 as its peer. Earlier Svelte 5 versions already failed to compile its components, and package managers now warn at install time.

Before you upgrade

Vendor switches need a declared vendor. If you relied on 3.0.0's helper vendor switches, declare at least one vendor in the vendors option or on your backend. The dialog then lists your helpers' vendors too. This changes only the dialog, not when scripts load. Declare the vendors.

Custom IAB translations need two keys. IABPreferenceCenterTranslations.vendorList adds adTechProvidersHeading and adTechProvidersNotice. Every built-in locale has them. Custom translation sets fail type checks until you add both.

__tcfapi callbacks take a union. Callbacks now receive TCData | TCDataOutsideTCF, and TCData.gdprApplies is true. Check gdprApplies before you read tcString.

Self-hosted backends run a migration. Migration 9 adds the consent.addtlConsent column, and the backend reads it on every consent save. Run migrations before you deploy 3.1, whether or not you use Additional Consent.

Packages

c15t, @c15t/core, @c15t/react, @c15t/nextjs, @c15t/tanstack-start, @c15t/vue, @c15t/svelte, @c15t/astro, @c15t/browser, @c15t/iab, @c15t/integrations, @c15t/backend, @c15t/cli, @c15t/node-sdk, @c15t/schema, @c15t/translations and @c15t/dev-tools release as 3.1.0. @c15t/ui and @c15t/scripts release as 3.0.1.