Skip to main content

Astro Components

ConsentScript

Render ConsentScript in the layout head

Render ConsentScript once in the <head> of the layout that wraps every page:

src/layouts/base.astro
---
import { ClientRouter } from 'astro:transitions';
import {
	ConsentBanner,
	ConsentDialog,
	ConsentDialogLink,
	ConsentScript,
} from 'c15t/astro/components';

interface Props {
	title: string;
}

const { title } = Astro.props;
---

<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta content="width=device-width, initial-scale=1" name="viewport" />
		<title>{title}</title>
		<ConsentScript />
		<ClientRouter />
	</head>
	<body>
		<slot />
		<footer>
			<ConsentDialogLink>Privacy settings</ConsentDialogLink>
		</footer>
		<ConsentBanner />
		<ConsentDialog />
	</body>
</html>

ConsentScript takes no props and ships no bundled JavaScript. Everything it renders is inline, so it works before any module script has loaded.

What it renders

ConsentScript renders up to three elements from Astro.locals.c15t:

ElementWhenDoes
Color-scheme <script>colorScheme is 'system' or 'dark'Sets the c15t-dark class on <html> before first paint
<style id="c15t-theme">The integration sets themeHolds the --c15t-* custom properties for your tokens
Boot payload <script type="application/json" data-c15t-config>AlwaysHands the browser the consent decision the server resolved

The boot payload is a JSON data block holding the resolved consent configuration: the policy, the visitor's stored consent and the translations for the negotiated language. The browser starts from it, so a page rendered on the server needs no /init request and the banner does not flicker. The payload escapes <, so translated text cannot close the script tag.

With colorScheme: 'light' or 'none', there is no color-scheme script. Light is the absence of the class, and 'none' leaves the class to your site. See set light or dark mode.

Rendered once per request

ConsentBanner, IABConsentBanner and ConsentScript all need the boot payload, and a page often contains more than one of them. The first of these components to render on a request emits the elements. The rest emit nothing.

Keep ConsentScript in <head> even though the banner would emit the same elements. In <head>, the color-scheme script runs before the browser paints anything, so a visitor whose system is dark never sees a light banner for a frame.

On prerendered and skipped routes

On a prerendered page, the boot payload carries no visitor state: no stored consent, no clock and no privacy signal. The browser reads the visitor's own cookie instead. See prerender pages in a server build.

On a route listed in middleware.skip, Astro.locals.c15t is unset and ConsentScript renders nothing. The page then starts without a server decision, and the browser requests one.

Content Security Policy

The color-scheme script and the theme <style> are inline code. They carry Astro.locals.c15t.nonce when your middleware sets it, and under Astro's security.csp the integration adds their hashes to the policy. The boot payload is a data block that the browser never runs, so a policy does not need to allow it. See Content Security Policy.

Next steps

  • ConsentBanner renders the same elements when ConsentScript is missing.
  • Integration options covers theme and colorScheme.
  • Server API documents buildConfigJSON, buildThemeCSS and buildColorSchemeScript, the helpers behind these elements.