Skip to main content

SvelteKit Components

ConsentRoot

Mount ConsentRoot in the root layout

Export loadConsent as the root layout's server load, then pass its consent to ConsentRoot as state:

src/routes/+layout.server.ts
export { loadConsent as load } from '@c15t/svelte/kit';
src/routes/+layout.svelte
<script lang="ts">
	import { posthog } from '@c15t/integrations/posthog';
	import {
		ConsentBanner,
		ConsentDialog,
		ConsentDialogLink,
		ConsentRoot,
	} from '@c15t/svelte';

	let { children, data } = $props();

	const scripts = [
		posthog({
			id: 'phc_your_project_key',
			initOptions: { cookieless_mode: 'never' },
			loadMode: 'after-consent',
		}),
	];
</script>

<ConsentRoot state={data.consent} {scripts}>
	{@render children()}
	<footer>
		<ConsentDialogLink>Privacy settings</ConsentDialogLink>
	</footer>
	<ConsentBanner />
	<ConsentDialog />
</ConsentRoot>

Pass data.consent through unchanged. It holds the visitor's server-resolved consent, the mode c15tHandle() was given, the backend URL and the consent route's prefix. ConsentRoot renders ConsentProvider from it, on the server and in the browser, so both render the same banner and the browser makes no second policy request. The quickstart explains c15tHandle and loadConsent.

ConsentRoot turns the mode into a transport that loads each policy path only when it runs. A page the server resolved ships a small transport that saves choices, and no resolver, policy rules, snapshot or other languages. The code to resolve the policy again in the browser, such as after a language change or on a prerendered page, loads when it is needed.

To use a transport of your own, pass custom() from @c15t/svelte as ConsentRoot's mode prop. c15tHandle() takes only data modes.

Keep the provider in the root +layout.svelte. A layout stays mounted across client-side navigation, so the runtime, the loaded scripts and the dialog survive page changes. A provider in a page component would start again on every navigation.

Create scripts and callbacks in the layout component, not in +layout.server.ts. They hold functions, which a server load cannot send to the browser.

Props

Pass each option as a top-level prop, or group them in options. When both set the same option, the top-level prop wins. The ConsentManagerOptions type from @c15t/svelte lists every option. SvelteKit's ConsentRoot takes the same props, except that state replaces mode and prefetch.

PropTypeDefaultBehavior
modemanifest(), hosted(), offline() or custom() result, all from @c15t/svelterequiredWhere policies come from and where choices are saved. Read once; remount the provider to change it. ConsentRoot builds it from state, or takes a custom() transport as mode.
prefetchConsentStatenoneServer-resolved state, such as the result of resolveConsent. With a resolved policy in it, the first render already knows whether to show the banner, and the browser skips its own policy request. ConsentRoot passes it from state. Read once.
scriptsScript[]noneVendor scripts that load when their category is allowed. Updates after mount: new scripts go to the loader. See scripts.
consentCategoriesAllConsentNames[]the policy's categoriesCategories the preference UI offers, within the policy's scope. Updates after mount.
themeThemenoneSlot classes and consentActions button styles. Design tokens in it are not applied in the browser. Updates after mount.
presentationConsentPresentationthe policy's defaultsBanner and dialog shape, position, button layout and blocking for every component. Updates after mount.
legalLinksLegalLinksnonePrivacy policy, cookie policy and terms links shown in the banner and dialog. Updates after mount.
i18nPartial<I18nConfig>bundled EnglishCopy overrides per language. They win key by key over the backend's copy for the same language; see translations. Read once.
colorScheme'light', 'dark', 'system' or nullnoneToggles the c15t-dark class on <html>. null or unset leaves <html> alone. Updates after mount.
noStylebooleanfalseRenders every component without c15t's classes. A component's own noStyle wins.
disableAnimationbooleanfollows prefers-reduced-motionTurns off enter and exit transitions.
scrollLock, trapFocusbooleanfrom presentationDefaults for the banner and dialog. blocking on a component sets both.
preloadDialog'idle' or 'intent''idle'When ConsentDialog starts loading before its first open. See ConsentDialog.
networkBlockeroptions or falseoffHolds fetch and XHR requests to listed domains until their category is allowed. Updates after mount: new rules and enabled apply, and false removes the blocker. The blocker loads on demand. See network blocker.
iframeBlocker{ disableAutomaticBlocking?: boolean } or falseonGates iframes that carry data-category. Updates after mount: false removes the blocker and a new disableAutomaticBlocking rebuilds it. See embeds.
iabProviderIABOptions or falseoffIAB TCF settings. Read once. See IAB TCF.
callbacksConsentProviderCallbacksnoneonChoiceRecorded, onPermissionsChanged, onError and onBeforeConsentRevocationReload. The latest functions are called. See callbacks.
reloadOnConsentRevokedbooleantrueReloads the page after a save withdraws a category or vendor that was granted.
overrides{ country?, region?, language?, gpc? }noneForces the location, language or Global Privacy Control signal the policy resolves for. A change after mount resolves the policy again.
userUsernoneIdentity sent with the policy request and every saved choice, so the choice is linked to your user ID. A different user after mount is identified with the backend; the user you mount with is not identified separately.
storageConfigStorageConfigcookie and key c15tNames and lifetime of the stored choice. Read once. On SvelteKit, pass the same name as cookieName to the server helpers.
persistenceboolean or optionstruefalse keeps choices in memory only. Read once.
clearOnRevocationClearOnRevocationConfigoffDeletes first-party cookies and storage entries when their category is withdrawn. Loads on demand. See clearing data on revocation. Read once.
vendorsVendor[]noneVendors offered for vendor-level consent outside IAB. See vendor consent. Updates after mount: a new list replaces the declared vendors.
noncestringnoneContent Security Policy nonce for the <script> elements the script loader adds. See Content Security Policy. Read when the loader starts.
enabledbooleantruefalse grants every category, hides all consent UI and skips /init. Updates after mount: turning it off renders a separate permissive state and keeps the visitor's stored choice for when it is turned back on.
runtimeConsentRuntimenoneA runtime you created with createConsentRuntime(). See share one runtime.
optionsConsentManagerOptionsnoneThe same options as one object.
childrenSnippetnoneYour app. The provider renders no markup of its own.

"Read once" options are read when the provider is created. Changing them later has no effect until the provider remounts, and outside production a change to mode, i18n or experiment logs a warning. Options that update after mount are compared with their previous values, so a new options object that only changes theme sends no request. The provider has no policyRules option. For local policy rules, pass offline({ policyRules }) as mode.

What the provider does when it mounts

The provider creates the consent runtime while the component initializes, on the server and in the browser. Creating it has no side effects, so the server can render the banner from prefetch. In the browser, onMount starts the runtime:

  1. It reads the stored choice from the cookie and local storage.
  2. It starts the script loader, the iframe blocker and, if configured, the network blocker and the IAB TCF add-on.
  3. It resolves the policy with its mode, unless prefetch already holds one.

When the provider unmounts, it stops all of them. Keep one provider at the root of the app, so it stays mounted across navigation and every component can reach it.

With networkBlocker, matching requests are held from the moment the provider is created in the browser, before its children run their own code, until the blocker decides them.

Share one runtime

Pass runtime when two component trees that cannot share Svelte context need the same consent state. Create it with createConsentRuntime() from @c15t/svelte, pass it to each provider, and call runtime.start() and runtime.dispose() yourself. A provider never starts or disposes a runtime it did not create. Options that build the runtime, such as mode, scripts and callbacks, come from your createConsentRuntime() call; display options such as theme and noStyle still apply per provider.

Verify the provider

Open the page in a private window with DevTools open:

  1. The banner appears once the policy resolves. window.c15t in the console shows pkg: '@c15t/svelte'.
  2. The Network panel shows the request your mode makes: none for manifest() with a bundled snapshot, one /init for hosted(), and none when the page came with a server-resolved state.
  3. A component that calls getConsentManager() renders without the c15t: no v3 consent context error.