Documentation
Everything you need to add compliant cookie consent to your site.
Installation
Script Tag (Recommended)
Add this script tag to your HTML, just before the closing </body> tag:
<script src="https://www.safebanner.com/safebanner.js"></script>NPM
For build-tool workflows:
npm install @safebanner/scriptimport '@safebanner/script';
// or with configuration
import { SafeBanner } from '@safebanner/script';
const manager = new SafeBanner({
position: 'bottom-right',
theme: 'dark',
});
manager.init();Self-Hosted
Download the script and host it yourself:
curl -o safebanner.js https://www.safebanner.com/safebanner.jsQuick Start
Get compliant in under 2 minutes:
- Add the script
<script src="https://www.safebanner.com/safebanner.js"></script> - That's it
The banner appears automatically for new visitors. Consent is stored locally and cookies are blocked until approved.
GDPR Mode (Default): Non-essential cookies are blocked until the user explicitly consents. This is the safest default for EU compliance.
Configuration
Configure via data attributes on the script tag:
<script
src="https://www.safebanner.com/safebanner.js"
data-position="bottom-right"
data-theme="dark"
data-color="#8b5cf6"
data-company="Acme Inc"
data-privacy="https://acme.com/privacy"
></script>Options Reference
| Attribute | Values | Default | Description |
|---|---|---|---|
data-position | bottom, top, bottom-left, bottom-right | bottom | Banner position on screen |
data-theme | light, dark | light | Color scheme |
data-color | Any hex color | #2563eb | Primary button color |
data-company | String | We | Your company name in banner text |
data-privacy | URL | — | Link to your privacy policy |
Cookie Categories
We automatically categorize cookies into three groups:
Necessary
Essential cookies required for the site to function. Always enabled.
Examples: Session IDs, CSRF tokens, auth cookies
Analytics
Cookies used to understand how visitors interact with your site.
Examples: Google Analytics (_ga, _gid), Mixpanel, Amplitude
Marketing
Cookies used for advertising and retargeting.
Examples: Facebook Pixel (_fbp), Google Ads (_gcl), LinkedIn (li_)
JavaScript API
Access the consent manager programmatically via window.safeBanner.
getConsent()
Returns the current consent state, or null if not yet consented.
Returns: ConsentState | null
const consent = window.safeBanner.getConsent();
// { necessary: true, analytics: true, marketing: false, timestamp: 1704067200000 }hasConsented()
Check if the user has made a consent choice.
Returns: boolean
if (window.safeBanner.hasConsented()) {
// User has already chosen
}hasConsentFor(category)
Check if user has consented to a specific category.
Returns: boolean
if (window.safeBanner.hasConsentFor('analytics')) {
// Safe to load Google Analytics
loadGoogleAnalytics();
}updateConsent(updates)
Programmatically update consent preferences.
Returns: void
window.safeBanner.updateConsent({
analytics: true,
marketing: false
});reset()
Clear stored consent and show the banner again.
Returns: void
// Add a 'Manage Cookies' link in your footer
document.getElementById('manage-cookies').addEventListener('click', () => {
window.safeBanner.reset();
});show()
Manually show the consent banner.
Returns: void
window.safeBanner.show();hide()
Manually hide the consent banner.
Returns: void
window.safeBanner.hide();TypeScript Types
interface ConsentState {
necessary: boolean; // Always true
analytics: boolean;
marketing: boolean;
timestamp: number; // Unix timestamp of consent
}
type ConsentCategory = 'necessary' | 'analytics' | 'marketing';Examples
React / Next.js
// components/SafeBanner.tsx
'use client';
import Script from 'next/script';
export function SafeBanner() {
return (
<Script
src="https://www.safebanner.com/safebanner.js"
data-position="bottom-right"
data-theme="light"
strategy="afterInteractive"
/>
);
}
// app/layout.tsx
import { SafeBanner } from '@/components/SafeBanner';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<SafeBanner />
</body>
</html>
);
}Conditional Analytics Loading
// Only load Google Analytics if user consented
function loadAnalytics() {
if (window.safeBanner?.hasConsentFor('analytics')) {
const script = document.createElement('script');
script.src = 'https://www.googletagmanager.com/gtag/js?id=GA_ID';
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'GA_ID');
}
}
// Run on page load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadAnalytics);
} else {
loadAnalytics();
}WordPress
// Add to your theme's functions.php
function add_consent_manager() {
echo '<script src="https://www.safebanner.com/safebanner.js" data-company="' . get_bloginfo('name') . '"></script>';
}
add_action('wp_footer', 'add_consent_manager');Vue.js
<!-- App.vue -->
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
mounted() {
const script = document.createElement('script');
script.src = 'https://www.safebanner.com/safebanner.js';
script.dataset.position = 'bottom-right';
document.body.appendChild(script);
}
}
</script>Footer "Manage Cookies" Link
<footer>
<a href="#" id="manage-cookies">Manage Cookie Preferences</a>
</footer>
<script>
document.getElementById('manage-cookies').addEventListener('click', (e) => {
e.preventDefault();
window.safeBanner.reset();
});
</script>Self-Hosting
Want full control? Self-host the script on your own infrastructure.
Option 1: Download the Script
# Download the latest version
curl -o safebanner.js https://www.safebanner.com/safebanner.js
# Host it on your server
cp safebanner.js /var/www/html/js/Option 2: Build from Source
# Clone the repo
git clone https://github.com/yourusername/safebanner.git
cd safebanner
# Install dependencies
pnpm install
# Build the script
pnpm build
# Output is in packages/consent-script/dist/safebanner.jsRequirements
- No backend required for free tier (client-side only)
- Serve the JS file with proper CORS headers if cross-origin
- Recommended: serve via CDN for performance (Cloudflare, Vercel Edge, etc.)
License: MIT. Use it however you want, including in commercial projects.
Paid Features
The free tier covers most use cases. Paid tiers add features for teams that need audit trails and multi-site management.
| Feature | Free | Starter | Pro | Enterprise |
|---|---|---|---|---|
| Consent banner | ✓ | ✓ | ✓ | ✓ |
| Local consent storage | ✓ | ✓ | ✓ | ✓ |
| GDPR mode | ✓ | ✓ | ✓ | ✓ |
| Self-host | ✓ | ✓ | ✓ | ✓ |
| Hosted consent logs | — | ✓ | ✓ | ✓ |
| Dashboard | — | ✓ | ✓ | ✓ |
| Multi-domain | — | — | ✓ | ✓ |
| Audit exports | — | — | ✓ | ✓ |
| Compliance alerts | — | — | ✓ | ✓ |
| Geo-based rules | — | — | ✓ | ✓ |
| SLA | — | — | — | ✓ |
| SSO | — | — | — | ✓ |
FAQ
Is this really GDPR compliant?
What about CCPA?
How do you detect cookies?
document.cookie and match against known patterns (Google Analytics, Facebook Pixel, etc.). We categorize them as necessary, analytics, or marketing. You can also manually configure categories.Does this block cookies automatically?
Can I customize the banner text?
How do I add a 'Manage Cookies' link?
window.safeBanner.reset() to clear consent and re-show the banner. See the Examples section above.Troubleshooting
Banner not appearing
- Check the browser console for errors
- Ensure the script is loaded (Network tab → filter by "consent")
- Clear localStorage (
localStorage.removeItem('safebanner_consent')) - You may have already consented — try
window.safeBanner.reset()
Cookies still being set before consent
- Ensure our script loads before other scripts (analytics, ads, etc.)
- Some cookies are set server-side — we can only block client-side cookies
- Check if the cookie is categorized as "necessary"
Script not loading (CORS error)
- If self-hosting, ensure your server sends proper CORS headers
- Use our CDN (
www.safebanner.com) which handles CORS automatically
Banner style conflicts
- Our styles are scoped with
.cm-prefix to avoid conflicts - Check for CSS resets or aggressive global styles in your app
- Use browser dev tools to inspect the banner element
Still stuck? Open an issue on GitHub and we'll help.