CookieConsentBanner
The cookie consent banner component, how SysthemaProvider mounts it, and its config, theme, layout and reopen link.
On this page
CookieConsentBanner shows the cookie consent banner and preferences modal, built on vanilla-cookieconsent (opens in new tab) and themed by the cookieConsent tokens. It renders no DOM of its own: it starts the consent library on the client, which adds its markup to <body>. You rarely render it yourself, because SysthemaProvider mounts it whenever it receives an enabled config.
import { CookieConsentBanner, Heading, Paragraph } from '@systhemaui/next'
import type { SysthemaCookieConsentConfig } from '@systhemaui/core'
const config: SysthemaCookieConsentConfig = {
enabled: true,
cookie: { name: 'docs_demo_consent' },
hideFromBots: false,
categories: {
necessary: { readOnly: true, enabledByDefault: true },
analytics: { enabledByDefault: false },
},
policyLinks: [{ label: 'Privacy policy', url: '#privacy' }],
translations: {
en: {
consentModal: {
title: 'We use cookies',
description: 'Some keep the site working, others help us understand how it is used.',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Necessary only',
showPreferencesBtn: 'Choose',
},
preferencesModal: {
title: 'Cookie preferences',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Necessary only',
savePreferencesBtn: 'Save choices',
sections: {
necessary: { title: 'Necessary', description: 'Required for the site to work.' },
analytics: { title: 'Analytics', description: 'Anonymous usage statistics.' },
},
},
},
},
}
export default function Demo() {
return (
<>
<Heading.h3>Northwind</Heading.h3>
<Paragraph>
Made your choice already?{' '}
<a className="link" href="#cookie-settings">
Cookie settings
</a>{' '}
opens the preferences again.
</Paragraph>
<CookieConsentBanner config={config} />
</>
)
}ImportLink to this section
import { CookieConsentBanner } from '@systhemaui/next'In a React app without Next.js, import it from @systhemaui/react.
UsageLink to this section
Through the providerLink to this section
Pass the resolved config to SysthemaProvider. getSysthemaCookieConsentConfig() reads cookieConsent from systhema.config.ts, merges the cookies found by the scanner, and returns null while enabled is not true, in which case nothing mounts:
import type { ReactNode } from 'react'
import { SysthemaProvider, getSysthemaCookieConsentConfig } from '@systhemaui/next'
export default function RootLayout({ children }: { children: ReactNode }) {
const cookieConsent = getSysthemaCookieConsentConfig()
return (
<html lang="en">
<body>
<SysthemaProvider cookieConsent={cookieConsent}>{children}</SysthemaProvider>
</body>
</html>
)
}In a Payload project, RootLayout from @systhemaui/payload resolves the config from the General Settings global on the server and passes it on, so editors control the texts, links and theme. See Cookie consent.
On its ownLink to this section
Render the component directly when you don't use the provider. Mount it once, in a layout that persists across pages:
import { CookieConsentBanner, getSysthemaCookieConsentConfig } from '@systhemaui/next'
export function Consent() {
const config = getSysthemaCookieConsentConfig()
return config ? <CookieConsentBanner config={config} /> : null
}ExamplesLink to this section
ConfigLink to this section
The config prop is a SysthemaCookieConsentConfig. The keys that shape the banner:
| Key | What it does |
|---|---|
enabled | Must be true, or the component does nothing |
categories | Per category (necessary, functional, analytics, performance, advertisement): enabledByDefault and readOnly |
translations | The texts per language: consentModal, preferencesModal and its sections |
defaultLanguage | The language to start with (default en); the <html lang> wins when a translation exists for it |
policyLinks | Links composed into the consent modal footer |
theme | A color-system mode for the banner alone |
guiOptions | vanilla-cookieconsent's layout and position options, passed through |
reopenSelector | Elements that reopen the preferences (default a[href="#cookie-settings"]); false turns the listener off |
The full shape, including cookies, cookie and the scanner options, is on Configuration.
Theme and layoutLink to this section
theme re-themes the banner independently of the page: the component sets data-theme on the banner root (#cc-main), and the cookieConsent tokens follow. 'inherit', or no value, uses the page's theme. guiOptions picks vanilla-cookieconsent's layouts; the default is a box in the bottom right corner with a box preferences modal.
import { CookieConsentBanner, Heading, Paragraph } from '@systhemaui/next'
import type { SysthemaCookieConsentConfig } from '@systhemaui/core'
const config: SysthemaCookieConsentConfig = {
enabled: true,
cookie: { name: 'docs_demo_consent_bar' },
hideFromBots: false,
theme: 'dark',
guiOptions: {
consentModal: { layout: 'bar inline', position: 'bottom' },
preferencesModal: { layout: 'box' },
},
categories: {
necessary: { readOnly: true, enabledByDefault: true },
analytics: { enabledByDefault: false },
},
policyLinks: [{ label: 'Privacy policy', url: '#privacy' }],
translations: {
en: {
consentModal: {
title: 'We use cookies',
description: 'Some keep the site working, others help us understand how it is used.',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Necessary only',
showPreferencesBtn: 'Choose',
},
preferencesModal: {
title: 'Cookie preferences',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Necessary only',
savePreferencesBtn: 'Save choices',
sections: {
necessary: { title: 'Necessary', description: 'Required for the site to work.' },
analytics: { title: 'Analytics', description: 'Anonymous usage statistics.' },
},
},
},
},
}
export default function Demo() {
return (
<>
<Heading.h3>A light page with a dark banner</Heading.h3>
<Paragraph>
Made your choice already?{' '}
<a className="link" href="#cookie-settings">
Cookie settings
</a>{' '}
opens the preferences again.
</Paragraph>
<CookieConsentBanner config={config} />
</>
)
}Reopen linkLink to this section
The banner closes for good once the visitor chooses. Put a link to #cookie-settings in your footer so they can change their mind; a click on it opens the preferences modal. The demos above have one.
;<a href="#cookie-settings">Cookie settings</a>How it worksLink to this section
- vanilla-cookieconsent and its stylesheet are imported on the client only, inside an effect, so server rendering and Node tooling never load them. The
cc--systhemaclass lands on<html>after the stylesheet has loaded, and the core CSS keeps the banner hidden until then, so it never flashes unstyled. - The library is a page-wide singleton, so the component starts it once per page load. A second
CookieConsentBanner, a remount in React Strict Mode or a hot reload attaches only the reopen listener again; in development, a console warning reports another initializer that ran first. - The banner runs in opt-in mode and manages
<script type="text/plain" data-category="…">tags, which run once their category is accepted. See Gating third-party scripts. - Changing
config.themere-themes a mounted banner on the next render; other config changes take effect on the next page load.
Browser automationLink to this section
vanilla-cookieconsent hides the banner from bots by default (hideFromBots: true). Its check matches every automated browser through navigator.webdriver, so Playwright, Puppeteer and agent-driven QA see no banner and no error. The component logs [systhema] Cookie consent banner suppressed: … to the console when that happens. Set hideFromBots: false for an end-to-end run; it is only passed on when you set it, so the library's default stays in force everywhere else. The demos on this page set it so they render in screenshot tools too.
PropsLink to this section
| Prop | Type | Default | Description |
|---|---|---|---|
config (required) | SysthemaCookieConsentConfig | - |
HTML and CSSLink to this section
HTML projects get the same banner from the vanilla JS bundle: set the config on window.__SYSTHEMA_COOKIE_CONSENT__ before the bundle loads.
<script>
window.__SYSTHEMA_COOKIE_CONSENT__ = {
enabled: true,
policyLinks: [{ label: 'Privacy policy', url: '/privacy-policy' }],
}
</script>
<script src="node_modules/@systhemaui/core/dist/js/bundle.global.js" defer></script>
<footer>
<a href="#cookie-settings">Cookie settings</a>
</footer>The banner is styled by the cookieConsent block of the core CSS, scoped to .cc--systhema, from the cookieConsent tokens in colors and spacing. See Theming the banner.
Next.jsLink to this section
@systhemaui/next re-exports CookieConsentBanner from @systhemaui/react and getSysthemaCookieConsentConfig from the client-safe @systhemaui/core/client entry, so a Next.js app imports both from @systhemaui/next without pulling the token dataset into a client bundle. The component is a client component; render it from a Server Component layout as it is.
AccessibilityLink to this section
- vanilla-cookieconsent renders the banner and the preferences modal as dialogs with real buttons, operable from the keyboard.
- Policy links that open a new tab get
rel="noopener noreferrer"and an "(opens in new tab)" suffix in their accessible name, translatable withopensInNewTabLabel. - The close button of the preferences modal gets a translated label (
closeIconLabel), with a built-in default per language. - The banner's colors come from your tokens; check their contrast in both themes.