Skip to content

Default styled banner

@tickboxhq/banner-default ships <ConsentBannerDefault> and <ConsentNoticeDefault> — styled versions of the headless banner and notice components. Use these when you don’t want to spend time on a design.

The visual is GitHub-ish: system font, 6px corners, subtle border, soft shadow, light/dark via prefers-color-scheme. Equal-prominence Accept/Reject buttons (UK ICO requires this — see Concepts → Consent modes). Customise opens a modal with per-category toggles, focus trap, Escape-to-close.

Install

Terminal window
# in addition to the framework adapter you already have
npm install @tickboxhq/banner-default

React

import { ConsentProvider } from '@tickboxhq/react'
import { ConsentBannerDefault } from '@tickboxhq/banner-default/react'
import config from './consent.config'
export default function App() {
return (
<ConsentProvider config={config}>
{/* your app */}
<ConsentBannerDefault policyUrl={config.policy?.url} />
</ConsentProvider>
)
}

For sites with only notice-mode categories (like UK DUAA-exempt analytics):

import { ConsentNoticeDefault } from '@tickboxhq/banner-default/react'
<ConsentNoticeDefault policyUrl="/privacy" />

Vue / Nuxt

<script setup lang="ts">
import { ConsentBannerDefault } from '@tickboxhq/banner-default/vue'
import config from './consent.config'
</script>
<template>
<ClientOnly>
<ConsentBannerDefault :policy-url="config.policy?.url" />
</ClientOnly>
</template>

Languages

Built-in translations: en, de, fr, es, it, nl, pt, pl, uk. Pass any BCP-47 tag — 'fr-CH' falls back to 'fr', 'pt-BR' to 'pt', 'uk-UA' to 'uk', unknown locales to English.

<ConsentBannerDefault locale="de" policyUrl="/privacy" />

Or read from the browser at render time:

<ConsentBannerDefault locale="auto" />

'auto' reads navigator.language. On the server (or anywhere navigator is missing) it falls back to English, so you usually want this rendered client-side only — wrap in <ClientOnly> on Nuxt or behind a useEffect mount flag in Next.js if you SSR.

For more control — i18n libraries, server-side detection, runtime tag switching — see the i18n recipe.

Customise the copy

copy overrides individual strings on top of whichever locale you’ve chosen. They compose: pick a language, override one or two labels.

<ConsentBannerDefault
locale="de"
copy={{
acceptLabel: 'Klar, akzeptieren',
}}
/>

Or pass a fully custom set, ignoring the built-ins:

<ConsentBannerDefault
copy={{
title: 'Cookie preferences',
description: 'We use Google Analytics to understand site usage. You can accept or reject this.',
acceptLabel: 'Accept all',
rejectLabel: 'Reject all',
customiseLabel: 'Customise',
saveLabel: 'Save preferences',
closeLabel: 'Close',
policyLinkLabel: 'Privacy policy',
requiredBadge: 'Required',
}}
/>

Theming

Use CSS custom properties to brand without forking. The styles bind to a .tb-root class on every component:

.tb-root {
--tb-radius: 12px;
--tb-link: #6366f1;
}

Apply brand colour to both Accept and Reject by overriding .tb-btn-equal (their shared class), not --tb-primary-bg — that’s reserved for the modal Save button:

.tb-root .tb-btn-equal {
background: #6366f1;
color: #fff;
border-color: #6366f1;
}

ICO and EDPB treat unequal visual weight on the first-layer Accept/Reject buttons as a dark pattern. Don’t break that.

Bundle cost

  • React entry: ~8 KB unminified ESM
  • Vue entry: ~7 KB unminified ESM
  • Plus a shared chunk (~8 KB) containing CSS and copy constants, loaded once

After gzip, expect 4–5 KB per framework entry.