Initialization Flow
What happens, in order, when a page loads.
1. Plugin mount (client only)
nuxt-c15t registers plugin.client.ts, which runs once per client navigation lifecycle. It calls getOrCreateConsentRuntime() from c15t, which creates the store the first time and returns the cached instance on subsequent invocations.
Server renders never create a store — all c15t state is client-only. The useC15tConsent.server composable reads the consent cookie directly instead.
2. Runtime + store creation
getOrCreateConsentRuntime():
- Builds the Zustand vanilla store with its initial state.
- Reads any existing consent from
localStorage/ thec15tcookie. - Registers the iframe blocker and network blocker (if configured) so they're active before the first paint.
- Applies
consentCategories,overrides(includingcountryOverride), and anyofflinePolicy.policyPacks.
The store is provided to the Nuxt app as $c15tStore and surfaced through useC15t().
3. Policy resolution
In hosted / self-hosted mode, c15t fires GET /init against the configured backendURL. The prefetch script injected into <head> (enabled by default via the prefetchScript option) can start this request before the Nuxt bundle hydrates, eliminating waterfalls.
In offline mode, c15t evaluates offlinePolicy.policyPacks locally using region > country > default precedence. See Policy Packs.
The resolved response includes:
model—opt-in,opt-out,iab, ornoneui—banner,dialog, ornonecategories— allowed / default categories for the matched jurisdiction- A policy fingerprint — if it changes, consent re-prompts
4. Banner decision
The banner renders only when all of these are true:
- The resolved
ui.modeisbannerordialog. - No prior consent exists, or the stored fingerprint no longer matches the resolved fingerprint.
activeUIhasn't been forced to'none'.
For opt-out jurisdictions the default UI is none — tracking is legal without a blocking banner, so nothing renders unless you explicitly ask for it.
5. Gating takes effect
Once the store settles, gated integrations activate:
<C15tIframe>mounts the iframe when its category is granted.- Raw
<iframe data-category>elements get theirsrcrestored when consent lands. - The network blocker short-circuits matching
fetch/XHRcalls until categories are granted. <NuxtScript>consumers usinguseC15tScriptTriggerwait for their consent promise to resolve.
SSR and hydration
Nothing hydrates from the server because the plugin is .client. The upside: no SSR mismatch on consent state — it simply doesn't exist during render. The downside: if you need server-rendered content to branch on consent, use the useC15tConsent server composable which reads the cookie directly.
The prefetch script (head position, hosted modes only) is the escape hatch for "I want /init to be in flight before my JS runs." It's safe to keep enabled.