Docs
Next

Consent configuration

Configure categories, translations, policy links and server resolution for the cookie banner.

On this page

Configure the banner through cookieConsent in systhema.config.ts. In Payload projects, the exposed values seed General Settings; editors then control those fields. In Next.js projects without Payload, pass the resolved config to the provider.

systhema.configLink to this section

Merge this block into your existing configuration:

systhema.config.ts
import type { SysthemaConfig } from '@systhemaui/core'
import manifest from './src/tokens/manifest.json'

const config: SysthemaConfig = {
  manifest,
  packages: { react: true },
  cookieConsent: {
    enabled: true,
    defaultLanguage: 'en',
    policyLinks: [
      { label: 'Privacy policy', url: '/privacy-policy' },
      { label: 'Cookie policy', url: '/cookie-policy' },
    ],
    scanner: {
      include: ['src/**/*.{ts,tsx,js,jsx}'],
      exclude: ['**/node_modules/**', '**/dist/**'],
    },
    translations: {
      en: {
        consentModal: { title: 'We use cookies' },
        preferencesModal: { title: 'Cookie preferences' },
      },
    },
  },
}

export default config

Retain your other package settings, custom tokens and design inputs. enabled defaults to false. You can gate it with process.env.NEXT_PUBLIC_COOKIE_CONSENT_ENABLED === 'true' if that variable is set in the build environment.

In Payload, also enable generalSettings.cookieConsent.enabled in the plugin options to register the editor tab. The editor's Enable switch can suppress the banner. If the tab is absent, RootLayout can fall back to the developer config; tab registration is not a third runtime gate. See Project setup.

Configuration shapeLink to this section

Use the exported type rather than maintaining a local copy:

import type {
  SysthemaCookieConsentConfig,
  SysthemaCookieCategoryConfig,
  SysthemaCookieCategoryId,
  SysthemaCookieConsentTranslation,
  SysthemaCookieEntry,
} from '@systhemaui/core'
OptionPurpose
enabledMaster switch, disabled by default
defaultLanguageFallback language, en by default
cookieConsent cookie name and expiresAfterDays; defaults to cc_cookie and 365
policyLinksFooter links with label, url and optional newTab
themeBanner mode, such as default or dark with default tokens; inherit uses the document theme
hideFromBotsPass false to display the banner during browser automation
guiOptionsPass-through layout options for vanilla-cookieconsent
reopenSelectorDefaults to a[href="#cookie-settings"]; false disables the click listener
categoriesOverrides for the five supported categories
cookiesCookie records with name, category, optional description, duration, party and domain
translationsCopy keyed by language code
scannerSource include and exclude globs and optional databaseUrl override

CategoriesLink to this section

IDDefault state
necessaryEnabled and read-only
functionalOff
analyticsOff
performanceOff
advertisementOff

Each category accepts label, description, readOnly and enabledByDefault. Custom categories are not supported. Keep optional categories opt-in unless the site's reviewed requirements say otherwise.

The integration does not map category changes to Google Consent Mode v2. If your tracking setup needs that protocol, implement and verify it separately; category-based script gating alone does not provide it.

TranslationsLink to this section

Override only the copy you need. Core merges the default English translation with the project config. Add a matching translation for every additional language you serve.

Cookie translation override
import type { SysthemaCookieConsentTranslation } from '@systhemaui/core'

export const englishConsentCopy: SysthemaCookieConsentTranslation = {
  opensInNewTabLabel: '(opens in new tab)',
  consentModal: {
    title: 'We use cookies',
    description: 'Choose which optional cookies this site can use.',
    acceptAllBtn: 'Accept all',
    acceptNecessaryBtn: 'Reject all',
    showPreferencesBtn: 'Customize',
  },
  preferencesModal: {
    title: 'Cookie preferences',
    closeIconLabel: 'Close cookie preferences',
    acceptAllBtn: 'Accept all',
    acceptNecessaryBtn: 'Reject all',
    savePreferencesBtn: 'Save preferences',
    cookieTableHeaders: { name: 'Name', description: 'Description', duration: 'Duration' },
    sections: {
      necessary: { title: 'Necessary', description: 'Required for this site to function.' },
      analytics: { title: 'Analytics', description: 'Measure visits when you allow them.' },
    },
  },
}

Assign this value to cookieConsent.translations.en. preferencesModal.closeIconLabel has a built-in label for the shipped languages. consentModal.closeIconLabel is opt-in: setting it creates a banner dismiss button.

On initialization, vanilla-cookieconsent prefers <html lang> when that language, or its two-letter prefix, has a translation. Otherwise it uses the configured default, falling back to an available translation if that default is missing. You can change language later through CookieConsent.setLanguage().

In Payload, translations exposed in the editor seed the global only on first initialization. Cleared editor fields stay cleared; the developer translation does not refill them. Keys without a matching editor field continue to use the developer config. See Editing consent in Payload.

policyLinks renders links in declaration order. New-tab links use target="_blank", rel="noopener" and an accessible new-tab cue. Internal Payload page references are resolved on the server through the site's locale-aware link helpers.

Set translations.<language>.consentModal.footer to literal HTML when you need custom footer markup. That override applies per language. Otherwise Systhema composes the footer from policyLinks.

Add a persistent reopener to your site footer separately:

<a href="#cookie-settings">Cookie settings</a>

Resolving the configLink to this section

For a Next.js app without Payload, use the server resolver:

src/app/layout.tsx
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 data-theme="default" className="bg-layout-main">
        <SysthemaProvider cookieConsent={cookieConsent}>{children}</SysthemaProvider>
      </body>
    </html>
  )
}

The resolver returns null when the master switch is off. On the server, it reads the merged config and .systhema/cookies.discovered.json; explicitly configured cookie names win over discovered entries. The browser implementation reads the injected in-memory config and performs no filesystem access.

For Payload, retain the starter's RootLayout and its payloadInstance prop. It resolves General Settings on the server and forwards the banner config. See Cookie consent for setup and the plain HTML limitation.