Quick start
Two ways in. The script tag defines a global cookieowl and starts on its own; the npm package exports the same instance.
<script src="https://cdn.jsdelivr.net/npm/cookieowl@0.1.0/dist/cookieowl.js"
data-lang="auto" data-policy-url="/privacy"></script>import { cookieowl } from 'cookieowl';
cookieowl.init({
lang: 'auto',
policyUrl: '/privacy',
version: '2026-10',
consentMode: true,
autoBlock: true,
onConsent(record) {
navigator.sendBeacon('/api/consent', JSON.stringify(record));
},
});
if (cookieowl.accepted('analytics')) startAnalytics();
cookieowl.on('change', ({ accepted }) => console.log(accepted));Options
Pass them to cookieowl.init(), or set window.cookieowlConfig before the script tag.
| Option | Type | Default | What it does |
|---|---|---|---|
categories | (string | Category)[] | ['necessary', 'preferences', 'analytics', 'marketing'] | Category ids or objects with label, description, required and services. necessary is always added and always on. |
lang | string | 'auto' | Banner language: pt-BR, en, es, fr or de. auto reads <html lang>, then the browser. |
template | 'lgpd' | 'gdpr' | lgpd for Portuguese, gdpr otherwise | Legal wording of the banner and the preferences dialog. |
policyUrl | string | none | Link to your privacy or cookie policy, shown in the banner and the dialog. |
version | string | number | 1 | Your policy version. Changing it shows the banner to everyone again. |
expiresDays | number | 365 | Days until a choice expires and the banner asks again. |
storage | 'cookie' | 'localStorage' | 'memory' | 'cookie' | Where the record is kept. memory forgets on reload (handy for demos and tests). |
cookie | { name, domain, path, sameSite } | { name: 'cookieowl', path: '/', sameSite: 'Lax' } | Cookie options. Set domain to share consent across subdomains. |
layout | 'box' | 'bar' | 'modal' | 'box' | Corner box, full width bar or centered modal with a focus trap. |
position | 'bottom' | 'top' | 'bottom-left' | 'bottom-right' | 'bottom-right' (box), 'bottom' (bar) | Where the banner sits. |
theme | 'auto' | 'light' | 'dark' | 'auto' | auto follows data-theme or a .dark class on the page, then the system setting. |
dir | 'auto' | 'ltr' | 'rtl' | 'auto' | Text direction; auto follows <html dir>. |
reopenButton | boolean | 'left' | 'right' | true (left) | The floating owl button that reopens the preferences. |
container | HTMLElement | string | document.body | Mount inside an element; the UI is then positioned inside it. |
text | Partial<Texts> | built in | Override any text, as a string or one string per language. |
consentMode | boolean | ConsentModeConfig | false | Google Consent Mode v2: waitForUpdate, mapping, regions, adsDataRedaction, urlPassthrough, advanced. |
autoBlock | boolean | AutoBlockConfig | false | Hold known trackers by URL: patterns, categories, ignore. |
respectGPC | boolean | true | Treat Global Privacy Control as a refusal of optional categories. |
respectDNT | boolean | false | Treat Do Not Track the same way. |
clearCookies | boolean | true | Erase the cookies of withdrawn categories. |
reloadOnWithdraw | boolean | (categories) => void | true | Reload when a category whose scripts already ran is withdrawn, or handle it yourself. |
autoShow | boolean | true | Show the banner on init when there is no valid choice. |
onConsent | (record) => void | none | Called with the record on every choice. Send it to your audit log. |
Script tag attributes
The most common options also work as data attributes on the script tag. They win over window.cookieowlConfig.
| Attribute | Same as |
|---|---|
data-lang | lang |
data-template | template |
data-policy-url | policyUrl |
data-version | version |
data-categories | categories, comma separated |
data-layout | layout |
data-position | position |
data-theme | theme |
data-dir | dir |
data-storage | storage |
data-expires-days | expiresDays |
data-container | container (a selector) |
data-consent-mode | consentMode: true, or "advanced" |
data-auto-block | autoBlock: true |
data-gpc="false" | respectGPC: false |
data-dnt | respectDNT: true |
data-reopen | reopenButton: "false", "left" or "right" |
data-manual | Skip the automatic init and call cookieowl.init() yourself |
Blocking markup
Any element with data-cookieowl waits for its categories. List several to require all of them.
<script type="text/plain" data-cookieowl="analytics" src="/stats.js"></script>
<script type="text/plain" data-cookieowl="analytics marketing">trackBoth();</script>
<script type="text/plain" data-cookieowl="analytics" data-cookieowl-type="module" src="/app-stats.mjs"></script>
<iframe data-cookieowl="marketing" data-cookieowl-name="YouTube"
data-cookieowl-src="https://www.youtube-nocookie.com/embed/ID" width="560" height="315"></iframe>
<img data-cookieowl="marketing" data-cookieowl-src="https://ads.example.com/pixel.gif" alt="">
<a href="#" data-cookieowl-open>Cookie settings</a>type="text/plain"keeps a script inert; the original type goes indata-cookieowl-type(for examplemodule).data-cookieowl-srcholds the URL of an iframe, image or script until consent.data-cookieowl-namenames an embed in its placeholder.data-cookieowl-openon any element opens the preferences on click.
Methods
| Method | What it does |
|---|---|
init(config) | Starts cookieowl and returns the instance. Consent Mode defaults and the blocker are installed at once, the UI when the DOM is ready. Calling it again replaces the configuration. |
accepted(category) | True when the category (or every category in an array) is accepted. Required categories are always true. |
acceptAll() | Accepts every category and records method accept-all. |
rejectAll() | Keeps only required categories and records method reject-all. |
save(choices) | Saves a map like { analytics: true } or a list of accepted ids. |
getConsent() | A copy of the stored record, or null before a choice. |
categories() | The configured categories with their labels in the current language. |
show() / hide() | Shows or hides the banner without recording a choice. |
showPreferences() / hidePreferences() | Opens or closes the preferences dialog. |
reset() | Forgets the choice, turns optional categories off and shows the banner again. |
on(event, fn) | Listens to an event and returns a function that stops listening. |
off(event, fn) | Stops listening. |
destroy() | Removes the UI, the observers and the createElement hook. |
Events
Listen with cookieowl.on(), or on document as cookieowl:change, cookieowl:consent and so on. With a dataLayer on the page, each choice also pushes a cookieowl_consent event for Tag Manager.
| Event | Detail | When |
|---|---|---|
change | { categories, accepted, changed, record } | A category was accepted or withdrawn. |
consent | ConsentRecord | The visitor made a choice (also when nothing changed). |
ready | { categories, accepted, changed, record } | init finished; the state comes from a stored choice or the defaults. |
show / hide | 'banner' | 'preferences' | The banner or the dialog opened or closed. |
reset | undefined | reset() was called. |
const off = cookieowl.on('change', ({ categories, changed }) => {
if (changed.includes('marketing') && !categories.marketing) removeAdSlots();
});
document.addEventListener('cookieowl:consent', (e) => console.log(e.detail.method));Consent Mode v2
Each Google consent type is granted when all of its categories are accepted.
cookieowl.init({
consentMode: {
waitForUpdate: 500,
regions: [{ region: ['US'], analytics_storage: 'granted' }],
mapping: { analytics_storage: ['analytics'], ad_storage: ['marketing'] },
adsDataRedaction: true,
urlPassthrough: false,
advanced: false,
},
});| Consent type | Categories |
|---|---|
ad_storage | marketing |
ad_user_data | marketing |
ad_personalization | marketing |
analytics_storage | analytics |
functionality_storage | preferences |
personalization_storage | preferences |
security_storage | necessary |
Auto blocking
Known trackers and their default categories. Add your own with autoBlock.patterns.
| Id | Name | Category |
|---|---|---|
gtag | Google Analytics | analytics |
gtm | Google Tag Manager | analytics |
google-ads | Google Ads | marketing |
meta-pixel | Meta Pixel | marketing |
clarity | Microsoft Clarity | analytics |
hotjar | Hotjar | analytics |
linkedin-insight | LinkedIn Insight Tag | marketing |
tiktok-pixel | TikTok Pixel | marketing |
youtube | YouTube | marketing |
youtube-api | YouTube | marketing |
vimeo | Vimeo | marketing |
cookieowl.init({
autoBlock: {
patterns: [{ id: 'chat', name: 'Support chat', category: 'preferences', match: /widget\.chat\.example/ }],
categories: { gtm: 'marketing' },
ignore: ['hotjar'],
},
});The consent record
interface ConsentRecord {
id: string; // random, kept across updates
version: string; // your policy version
timestamp: string; // ISO 8601
expires: string; // ISO 8601
categories: Record<string, boolean>;
method: 'accept-all' | 'reject-all' | 'custom' | 'signal';
signal?: 'gpc' | 'dnt';
lang: string;
page: string;
}Styling
The stylesheet is scoped under .cko and injected once. Override the tokens on .cko or set --seed on :root.
| Variable | Use |
|---|---|
--seed | Accent color of the buttons and switches (amber by default). The same variable themes Monsoon, so one color can theme both. |
--cko-font | Font family of the banner and dialog. |
--cko-bg, --cko-fg, --cko-muted | Surface, text and secondary text colors. |
--cko-line, --cko-soft | Borders and subtle backgrounds. |
--cko-primary, --cko-on-primary | Button color and the text on it (contrast picked automatically). |
--cko-ring | Focus ring color. |
:root { --seed: #2f6fed; }
.cko { --cko-font: Inter, sans-serif; }
.cko-btn { border-radius: 999px; }React and Next.js
cookieowl/react exports useConsent(category?), ConsentGate and useCookieOwl().
import { ConsentGate, useConsent, useCookieOwl } from 'cookieowl/react';
function Footer() {
const owl = useCookieOwl();
const all = useConsent(); // { necessary: true, analytics: false, ... }
return <button onClick={() => owl.showPreferences()}>Cookie settings</button>;
}Vue
cookieowl/vue exports CookieOwlPlugin, useConsent(category) and ConsentGate with a fallback slot.
<script setup>
import { useConsent } from 'cookieowl/vue';
const analytics = useConsent('analytics');
</script>
<template>
<ConsentGate category="marketing">
<iframe src="https://player.vimeo.com/video/123" title="Video" />
<template #fallback>Video hidden until marketing cookies are allowed.</template>
</ConsentGate>
<p v-if="analytics">Thanks for helping us improve the site.</p>
</template>