openconsent
v2.0.0
Published
Lightweight, dependency-free GDPR Consent Management Platform (CMP) with native Google Consent Mode v2 support. Drop-in cookie banner, script blocking, no monthly fees.
Maintainers
Readme
OpenConsent
A lightweight, dependency-free GDPR Consent Management Platform with native Google Consent Mode v2 support.
The free, open-source alternative to Cookiebot, OneTrust and Iubenda: full control over your consent management, no vendor lock-in, no monthly fees. One script tag, zero dependencies, no backend required.
- 🔒 GDPR first — no cookies before consent, 12-month expiry, URL/CSS sanitization
- 🎯 Google Consent Mode v2 — native, denied-by-default, zero configuration
- 🎭 Script blocking — hold tracking scripts until the user consents, hot-swap on change
- 🧩 Works everywhere —
<script>tag, npm (ESM + CommonJS), any framework - 🛡️ CSP friendly — nonce support
- 🔄 SPA ready — MutationObserver for dynamically added scripts
- 🌍 Multi-language — English and Italian included
- 📦 TypeScript types — shipped with the package
Part of a small web-compliance toolkit: pair it with AccessiScan to audit accessibility (WCAG 2.1 / EN 301 549) on the same sites.
Install
Option A — Script tag (no build step)
<script src="https://cdn.jsdelivr.net/npm/openconsent@2/dist/openconsent.min.js"></script>
<!-- pinned to a tag on GitHub instead of npm: -->
<script src="https://cdn.jsdelivr.net/gh/iAlias/[email protected]/dist/openconsent.min.js"></script>That's enough for the zero-config banner. Place it as the first script in <head>,
immediately after <title>, so trackers are blocked before they execute.
Option B — npm
npm install openconsent// ESM
import { createOpenConsent } from 'openconsent';
const cmp = createOpenConsent({
config: {
banner: {
privacyPolicyUrl: 'https://yoursite.com/privacy-policy',
cookiePolicyUrl: 'https://yoursite.com/cookie-policy'
}
}
});// CommonJS
const { createOpenConsent } = require('openconsent');In the browser build the singleton is exposed as window.OpenConsent
(window.RSCMP is kept as a backwards-compatible alias).
Quick start
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Your Website</title>
<!-- 1. Load OpenConsent (auto-initializes) -->
<script src="https://cdn.jsdelivr.net/npm/openconsent@2/dist/openconsent.min.js"></script>
</head>
<body>
<!-- 2. Category-tagged scripts stay blocked until consent is given -->
<script type="text/plain" data-category="analytics">
// Google Analytics (gtag.js), Plausible, Matomo, ...
</script>
<script type="text/plain" data-category="marketing">
// Meta Pixel, Google Ads, TikTok Pixel, ...
</script>
</body>
</html>On the first visit OpenConsent shows the banner, blocks every data-category script, and wires
up Google Consent Mode v2 automatically. When the user chooses, scripts in the consented
categories are unblocked without a page reload.
How script blocking works
Mark any script with type="text/plain" and a data-category:
| Category | Typical use |
| --- | --- |
| necessary | Essential functionality — always runs |
| analytics | Analytics and performance measurement |
| marketing | Advertising and remarketing |
| preferences | User preferences and settings |
<!-- Blocked until "analytics" is granted -->
<script type="text/plain" data-category="analytics">/* ... */</script>
<!-- Never blocked -->
<script data-category="necessary">/* ... */</script>OpenConsent also auto-detects and blocks common trackers (Google Analytics/gtag, Meta Pixel,
TikTok, Hotjar, Mixpanel, Amplitude, Clarity, DoubleClick and more), including scripts added
later by SPAs. Critical attributes (type="module", nonce, integrity, crossorigin) are
preserved when a script is unblocked.
Configuration
window.OpenConsent.init({
config: {
policyVersion: '1.0',
banner: {
position: 'bottom', // 'top' | 'bottom' | 'center'
layout: 'bar', // 'bar' | 'box' | 'modal'
primaryColor: '#0084ff',
backgroundColor: '#ffffff',
textColor: '#000000',
buttonTextColor: '#ffffff',
showLogo: false,
logoUrl: 'https://yoursite.com/logo.svg',
privacyPolicyUrl: 'https://yoursite.com/privacy-policy',
cookiePolicyUrl: 'https://yoursite.com/cookie-policy'
},
categories: [
{ id: 'necessary', name: 'Necessary', required: true, enabled: true },
{ id: 'analytics', name: 'Analytics', required: false, enabled: false },
{ id: 'marketing', name: 'Marketing', required: false, enabled: false },
{ id: 'preferences', name: 'Preferences', required: false, enabled: false }
],
translations: {
it: { title: 'Rispettiamo la tua privacy' /* ... */ },
en: { title: 'We respect your privacy' /* ... */ }
}
}
});You can also configure it entirely through the script tag:
<script
src="https://cdn.jsdelivr.net/npm/openconsent@2/dist/openconsent.min.js"
data-site-id="YOUR_SITE_ID"
data-api-url="https://your-api.example.com"
data-auto-init="true"></script>data-api-url is optional. Without it, OpenConsent runs fully client-side and never makes
a network request.
API reference
The singleton is available as window.OpenConsent in the browser, or created explicitly with
createOpenConsent() in a bundler.
| Method | Description |
| --- | --- |
| init(options?) | Initialize the CMP. Resolves when the config is loaded. |
| getConsent() | Current consent categories, or null if not chosen yet. |
| applyConsent(categories) | Apply categories programmatically (unblocks scripts, updates Consent Mode). |
| showPreferences() | Re-open the preferences panel. |
| resetConsent() | Clear consent and show the banner again. |
| getStatus() | Diagnostic snapshot: { initialized, siteId, consent, blockedScripts, bannerVisible }. |
| enableDebug() / disableDebug() / setDebugMode(bool) | Toggle verbose logging. |
| testConsentMode() | Log the current Google Consent Mode state. |
Events
window.OpenConsent.consentManager.on('consentUpdated', (categories) => {
console.log('Consent changed:', categories);
});Consent categories
interface ConsentCategories {
necessary: boolean;
analytics: boolean;
marketing: boolean;
preferences: boolean;
}Google Consent Mode v2
OpenConsent sets a denied-by-default state as early as possible and updates it when the user
chooses. No configuration required. The default denies ad_storage, ad_user_data,
ad_personalization, analytics_storage, functionality_storage and
personalization_storage, while granting security_storage.
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>Framework examples
'use client';
import { useEffect } from 'react';
export default function ConsentLoader() {
useEffect(() => {
import('openconsent').then(({ createOpenConsent }) => {
createOpenConsent();
});
}, []);
return null;
}Then react to changes:
window.OpenConsent.consentManager.on('consentUpdated', ({ analytics }) => {
if (analytics) loadAnalytics();
});See examples/gtm-implementation.html for a complete GTM
walkthrough, including how to trigger tags on consent changes and inspect Consent Mode state.
Open examples/basic.html in a browser for a working demo with test
buttons and a live consent-status readout.
Migrating from v1 (rs-cmp)
v2 renames the project to OpenConsent with no breaking change for existing embeds:
window.RSCMPstill works;window.OpenConsentis the new name.- Consent stored under
rs-cmp-consentis read and migrated automatically toopenconsent(localStorage) and the cookie is renamed transparently. - The old CDN path
dist/cmp.min.jsis still published for existing script tags. - The old
init(config)form still works;init({ siteId, apiUrl, config })is now supported.
Optional: consent logging backend
The SDK needs no backend. If you want to store consent records, the
server-side/ folder contains ready-to-adapt loggers:
node-logger.js— Node.js/Express + PostgreSQL examplephp-logger.php— PHP example
These examples have their own dependencies and are not installed with the package:
cd server-side && npm installDevelopment
npm install # install dev dependencies
npm run build # build IIFE (dev + min), legacy, CommonJS and ESM bundles
npm test # run the Jest suite
npm run lint # ESLintSource layout:
| File | Purpose |
| --- | --- |
| src/core.js | The library: all classes plus createOpenConsent(), no side effects |
| src/browser.js | Browser entry: script blocking, window.OpenConsent, auto-init |
| src/index.mjs | ESM entry re-exporting the core |
The committed dist/openconsent.min.js (and dist/cmp.min.js) are the published bundles; CI
fails if they drift from the source.
Security & compliance
- No cookies are set before consent.
- Consent is stored in
localStorage, with a minimal first-party presence cookie. - URLs and CSS color values are sanitized before being written to the DOM.
- Optional backend logging hashes IP addresses (SHA-256) before storage.
- Consent expires after 12 months and is re-requested.
License
MIT © Antonino Di Stefano
