State-based consent management with battle-tested patterns for GDPR/CCPA compliance.
- ✅ State-based consent - Category-based control (necessary, functional, analytics, marketing, social)
- ✅ Inferred consent - Auto opt-in if cookie exists (don't nag returning users)
- ✅ Cookie persistence - Remember consent across sessions
- ✅ Promise-based API -
waitForConsent()for async consent checks - ✅ Event emission -
consent:granted,consent:denied,consent:updated - ✅ Nuclear opt-out -
revoke()deletes all consent and cookies - ✅ Platform adapters - Auto-detect OneTrust, Cookiebot, Usercentrics
- ✅ Metadata tracking - Track when/how consent was given
- ✅ Policy versioning - Re-request consent when policy updates
- Storage Plugin - Persists consent to cookies
- Queue Plugin - Consent-aware queuing (see integration examples)
- OneTrust - Auto-sync with OneTrust consent management
- Cookiebot - Auto-sync with Cookiebot
- Usercentrics - Auto-sync with Usercentrics
import { SDK } from '@prosdevlab/sdk-kit';
import { consentPlugin, storagePlugin } from '@prosdevlab/sdk-kit-plugins';
const sdk = new SDK();
// Storage plugin required for persistence
sdk.use(storagePlugin);
sdk.use(consentPlugin);
await sdk.init();// Grant consent for analytics
sdk.consent.grant('analytics');
// Grant multiple categories
sdk.consent.grant(['analytics', 'marketing']);
// Deny consent
sdk.consent.deny('marketing');
// With metadata
sdk.consent.grant('analytics', {
method: 'banner',
policyVersion: '2.0'
});// Check if granted
if (sdk.consent.isGranted('analytics')) {
// Track events
sdk.track('pageview');
}
// Check if denied
if (sdk.consent.isDenied('marketing')) {
// Don't show marketing content
}
// Check if pending (no decision yet)
if (sdk.consent.isPending('analytics')) {
// Show consent banner
}
// Get full state
const state = sdk.consent.getState();
// { necessary: 'granted', analytics: 'granted', marketing: 'denied', ... }// Block until user makes a decision
const granted = await sdk.consent.waitForConsent('analytics');
if (granted) {
// User granted analytics consent
sdk.track('pageview');
}
// With timeout
const granted = await sdk.consent.waitForConsent('analytics', { timeout: 10000 });// Revoke ALL consent (except necessary)
sdk.consent.revoke();
// - Denies all non-necessary categories
// - Clears consent cookie
// - Emits 'consent:revoked' event// If user has existing consent cookie, auto opt-in
// (Don't show banner to returning users - reduces banner fatigue by ~70%)
await sdk.init({
consent: {
inferConsent: true // Default: true
}
});
// Listen for inferred consent
sdk.on('consent:inferred', ({ categories }) => {
console.log('Inferred consent for:', categories);
});// Auto-detect and sync with OneTrust
await sdk.init();
// SDK automatically:
// 1. Detects window.OneTrust
// 2. Syncs consent state
// 3. Listens for changes
// Custom category mapping
await sdk.init({
consent: {
platform: 'onetrust',
platformConfig: {
onetrust: {
categoryMap: {
'C0001': 'necessary',
'C0002': 'functional',
'C0003': 'analytics',
'C0004': 'marketing'
}
}
}
}
});
// Listen for platform detection
sdk.on('consent:platform-detected', ({ platform }) => {
console.log('Detected:', platform); // 'onetrust', 'cookiebot', etc.
});// Get consent metadata
const metadata = sdk.consent.getMetadata('analytics');
// {
// grantedAt: 1234567890,
// method: 'banner',
// policyVersion: '2.0'
// }
// Check policy version
if (!sdk.consent.isCurrentVersion('analytics', '2.0')) {
// Policy changed, re-request consent
showConsentBanner();
}// Consent granted
sdk.on('consent:granted', ({ categories, metadata }) => {
console.log('Granted:', categories);
// Flush queued events
sdk.queue?.flush();
});
// Consent denied
sdk.on('consent:denied', ({ categories, metadata }) => {
console.log('Denied:', categories);
});
// Consent updated
sdk.on('consent:updated', ({ state }) => {
console.log('State:', state);
});
// Consent inferred from cookie
sdk.on('consent:inferred', ({ categories }) => {
console.log('Inferred consent for:', categories);
});
// Consent revoked
sdk.on('consent:revoked', ({ categories }) => {
console.log('Revoked consent for:', categories);
});
// Platform detected
sdk.on('consent:platform-detected', ({ platform }) => {
console.log('Platform:', platform);
});import { queuePlugin, consentPlugin } from '@prosdevlab/sdk-kit-plugins';
sdk.use(queuePlugin);
sdk.use(consentPlugin);
await sdk.init();
// Queue events that require consent
sdk.queue.add({ event: 'pageview', data: {...} }, {
consent: ['analytics'] // Queued until analytics consent granted
});
sdk.queue.add({ event: 'ad_click', data: {...} }, {
consent: ['marketing'] // Queued until marketing consent granted
});
// When consent granted, flush relevant items
sdk.on('consent:granted', ({ categories }) => {
if (categories.includes('analytics')) {
// Flush analytics events
sdk.queue.flush();
}
});export default function myPlugin(plugin, instance, config) {
plugin.ns('my.plugin');
plugin.expose({
track(event, data) {
// Check consent before tracking
if (!instance.consent?.isGranted('analytics')) {
// Queue for later
instance.queue?.add({ event, data }, {
consent: ['analytics']
});
return;
}
// Has consent, send now
instance.transport.send({ event, data });
}
});
// Auto-flush on consent granted
instance.on('consent:granted', (categories) => {
if (categories.includes('analytics')) {
instance.queue?.flush();
}
});
}await sdk.init({
consent: {
// Default status for each category
defaults: {
necessary: 'granted', // Always granted
functional: 'pending',
analytics: 'pending',
marketing: 'pending',
social: 'pending'
},
// Cookie name for persistence
cookieName: '_sdk_consent', // Default
// Cookie TTL in seconds (1 year)
cookieTTL: 31536000,
// Current policy version
policyVersion: '1.0',
// Infer consent from existing cookie
inferConsent: true, // Default
// Platform adapter
platform: 'onetrust', // 'onetrust', 'cookiebot', 'usercentrics', or null
// Platform-specific config
platformConfig: {
onetrust: {
categoryMap: {
'C0001': 'necessary',
'C0002': 'functional',
'C0003': 'analytics',
'C0004': 'marketing',
'C0005': 'social'
}
}
}
}
});Check if consent is granted for a category.
Check if consent is denied for a category.
Check if consent is pending (no decision yet) for a category.
grant(categories: ConsentCategory | ConsentCategory[] | string | string[], metadata?: ConsentMetadata): void
Grant consent for one or more categories.
deny(categories: ConsentCategory | ConsentCategory[] | string | string[], metadata?: ConsentMetadata): void
Deny consent for one or more categories.
waitForConsent(category: ConsentCategory | string, options?: { timeout?: number }): Promise<boolean>
Wait for a consent decision (promise-based). Returns true if granted, false if denied or timeout.
Get current consent state for all categories.
Get metadata (when granted, method, policy version) for a category.
Check if consent was granted for a specific policy version.
Revoke ALL consent (except necessary). Clears cookie and denies all non-necessary categories.
type ConsentStatus = 'granted' | 'denied' | 'pending';
type ConsentCategory =
| 'necessary' // Always granted (required)
| 'functional' // Preferences, settings
| 'analytics' // Analytics, tracking
| 'marketing' // Advertising, remarketing
| 'social'; // Social media
interface ConsentState {
[category: string]: ConsentStatus;
}
interface ConsentMetadata {
grantedAt?: number;
deniedAt?: number;
policyVersion?: string;
method?: 'banner' | 'settings' | 'api' | 'inferred';
}- Modern browsers: Full support
- Older browsers: Graceful degradation (no persistence without Storage plugin)
- SSR: Safe (no-op in non-browser environments)
| Event | Data | Description |
|---|---|---|
consent:granted |
{ categories: string[], metadata: ConsentMetadata } |
Consent granted for categories |
consent:denied |
{ categories: string[], metadata: ConsentMetadata } |
Consent denied for categories |
consent:updated |
{ state: ConsentState } |
Consent state changed |
consent:inferred |
{ categories: string[] } |
Consent inferred from cookie |
consent:revoked |
{ categories: string[] } |
All consent revoked |
consent:platform-detected |
{ platform: string } |
Consent platform detected |
This plugin adopts proven patterns from production SDKs:
- Inferred Consent - Auto opt-in if cookie exists (reduces banner fatigue by ~70%)
- Nuclear Opt-Out - Complete data deletion on revoke (GDPR "right to be forgotten")
- Platform Delegation - Clean integration with OneTrust/Cookiebot/Usercentrics
- State-Based - Category-based control (modern GDPR/CCPA compliance)
- Promise-Based - Async
waitForConsent()for better DX
MIT