Events Documentation
Consent Pro V2 provides a comprehensive event system for tracking user interactions and consent state changes.
Table of Contents
- Event System Overview
- Subscribing to Events
- Event Types
- Custom DOM Events
- GTM Integration Events
- Event Patterns
Event System Overview
V2 features a robust event system that allows you to:
- Listen for consent changes
- Track user interactions with the banner
- Monitor visibility state changes
- Integrate with analytics platforms
- Debug and troubleshoot issues
All events fire through multiple channels:
- API event listeners - Via
on(),off(),once(),subscribe() - Custom DOM events - Dispatched on
documentwithconsentpro:prefix - GTM dataLayer events - For Google Tag Manager integration
Subscribing to Events
Recommended: Using Callbacks
The recommended way to subscribe to events is through the callback approach:
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
'consent',
async (instance) => {
// Subscribe to a specific event
instance.on('consent-updated', (data) => {
console.log('Consent updated:', data);
if (data.choices.analytics) {
initializeAnalytics();
}
});
// Fire only once
instance.once('banner-shown', (data) => {
console.log('Banner shown for the first time:', data);
});
// Subscribe to all events
const unsubscribe = instance.subscribe((event, data) => {
console.log(`Event: ${event}`, data);
// Send to analytics
analytics.track(`consentpro:${event}`, data);
});
// Later: unsubscribe
// unsubscribe();
},
]);Alternative: Direct API Access
You can also subscribe to events directly via window.FinsweetConsentProAPI (see API Reference for details):
// Basic subscription
window.FinsweetConsentProAPI.on('consent-updated', (data) => {
console.log('Consent updated:', data);
});
// One-time subscription
window.FinsweetConsentProAPI.once('ready', (data) => {
console.log('Initialized:', data);
});
// Universal subscription
const unsubscribe = window.FinsweetConsentProAPI.subscribe((event, data) => {
console.log(`Event: ${event}`, data);
});Note: Majority of examples in this documentation use the direct access approach. Both methods work identically and callback approach is recommended for most use cases.
Event Types
ready
Fired when Consent Pro is fully initialized and ready to use.
When: After initialization completes Frequency: Once per load
Event Data:
{
version: string; // Script version
instanceCount: number; // Number of instances
instance: string; // Active instance key
config: InstanceSettings; // Instance configuration
detectedRegion: string; // Detected user region
initTime: number; // Initialization timestamp
}Example:
window.FinsweetConsentProAPI.on('ready', (data) => {
console.log('Consent Pro v' + data.version + ' initialized');
console.log('Region detected:', data.detectedRegion);
console.log('Init time:', data.initTime);
console.log('Instance:', data.instance);
});Use Cases:
- Wait for initialization before checking consent
- Log initialization metrics
- Display region-specific messages
- Initialize dependent systems
consent-updated
Fired whenever consent choices change.
When: User accepts/rejects, programmatic update, preference changes Frequency: Every consent change
Event Data:
{
instanceKey: string;
consent: ConsentRecord; // New consent record
choices: ConsentChoices; // New choices
previousChoices?: ConsentChoices; // Previous state
source: 'banner' | 'preferences' | 'form' | 'api';
record: ConsentRecord; // Same as consent
consentModes?: Record<string, 'granted' | 'denied'>; // GTM consent modes
}Example:
window.FinsweetConsentProAPI.on('consent-updated', (data) => {
console.log('Consent ID:', data.consent.id);
console.log('New choices:', data.choices);
console.log('Previous choices:', data.previousChoices);
console.log('Changed via:', data.source);
console.log('GTM consent modes:', data.consentModes);
// Check what changed
if (data.choices.analytics !== data.previousChoices?.analytics) {
console.log('Analytics consent changed');
if (data.choices.analytics) {
initializeAnalytics();
}
}
if (data.choices.marketing && !data.previousChoices?.marketing) {
console.log('Marketing consent newly granted');
loadMarketingScripts();
}
});Use Cases:
- Initialize analytics when consent granted
- Load marketing scripts conditionally
- Update UI based on consent state
- Sync consent to backend
- Track consent changes in analytics
consent-loaded
Fired when existing consent is loaded from storage.
When: On initialization, if consent exists in storage and hasn't expired Frequency: Once per load (if consent exists)
Event Data:
{
instanceKey: string;
consent: ConsentRecord;
}Example:
window.FinsweetConsentProAPI.on('consent-loaded', (data) => {
console.log('Loaded existing consent:', data.consent);
console.log('User already consented on:', new Date(data.consent.timestamp));
// Initialize systems based on stored consent
if (data.consent.choices.analytics) {
initializeAnalytics();
}
});Use Cases:
- Fast-path initialization for returning users
- Restore user preferences
- Skip banner display for returning users
- Initialize services immediately
consent-reset
Fired when consent is reset to defaults.
When: User resets preferences, programmatic reset, expired consent Frequency: When reset occurs
Event Data:
{
instanceKey: string;
consent: ConsentRecord; // New default consent
}Example:
window.FinsweetConsentProAPI.on('consent-reset', (data) => {
console.log('Consent reset for:', data.instanceKey);
console.log('New defaults:', data.consent.choices);
// Clean up analytics
if (window.gtag) {
gtag('consent', 'update', {
analytics_storage: 'denied',
ad_storage: 'denied',
});
}
// Show banner again
window.FinsweetConsentProAPI.showBanner();
});Use Cases:
- Clean up analytics sessions
- Reset tracking scripts
- Show banner after reset
- Log reset events
banner-shown
Fired when the consent banner becomes visible.
When: Banner is displayed to user Frequency: Each time banner is shown
Event Data:
{
instanceKey: string;
reason: 'first-visit' | 'manual' | 'api' | 'expired';
}Example:
window.FinsweetConsentProAPI.on('banner-shown', (data) => {
console.log('Banner shown:', data.instanceKey);
console.log('Reason:', data.reason);
// Track banner impression
analytics.track('consent_banner_shown', {
instance: data.instanceKey,
reason: data.reason,
timestamp: Date.now(),
});
// Pause video if banner shown
if (data.reason === 'api') {
pauseBackgroundVideo();
}
});Use Cases:
- Track banner impressions
- Pause media when banner shows
- Adjust page layout
- Analytics tracking
banner-hidden
Fired when the consent banner is hidden.
When: Banner dismissed or hidden Frequency: Each time banner is hidden
Event Data:
{
instanceKey: string;
reason: 'manual' | 'api' | 'consent-given';
}Example:
window.FinsweetConsentProAPI.on('banner-hidden', (data) => {
console.log('Banner hidden:', data.instanceKey);
console.log('Reason:', data.reason);
// Resume media
if (data.reason === 'consent-given') {
resumeBackgroundVideo();
}
// Track banner dismissal
analytics.track('consent_banner_hidden', {
reason: data.reason,
});
});Use Cases:
- Resume paused media
- Restore page layout
- Track dismissal events
- Enable page interactions
preferences-shown
Fired when preferences modal opens.
When: User opens detailed consent preferences Frequency: Each time modal is opened
Event Data:
{
instanceKey: string;
source: 'api' | 'button' | 'link';
}Example:
window.FinsweetConsentProAPI.on('preferences-shown', (data) => {
console.log('Preferences opened:', data.instanceKey);
console.log('Opened via:', data.source);
// Track modal opening
analytics.track('consent_preferences_opened', {
source: data.source,
timestamp: Date.now(),
});
// Disable page scroll
document.body.style.overflow = 'hidden';
});Use Cases:
- Track preference engagement
- Disable page scroll
- Show help tooltips
- Load preference UI components
preferences-hidden
Fired when preferences modal closes.
When: User closes preferences without saving or after saving Frequency: Each time modal is closed
Event Data:
{
instanceKey: string;
action: 'api' | 'button' | 'save';
}Example:
window.FinsweetConsentProAPI.on('preferences-hidden', (data) => {
console.log('Preferences closed:', data.instanceKey);
console.log('Closed via:', data.action);
// Re-enable page scroll
document.body.style.overflow = '';
// Track modal closure
analytics.track('consent_preferences_closed', {
action: data.action,
saved: data.action === 'save',
});
});Use Cases:
- Re-enable page scroll
- Track modal engagement
- Clean up UI overlays
- Save analytics
donotsell-clicked
Fired when user interacts with "Do Not Sell" checkbox (CCPA).
When: User clicks DoNotSell checkbox (opt-out/informational modes only) Frequency: Each checkbox interaction
Event Data:
{
instanceKey: string;
mode: 'opt-out' | 'informational';
donotsellState: boolean;
}Example:
window.FinsweetConsentProAPI.on('donotsell-clicked', (data) => {
console.log('Do Not Sell clicked:', data.instanceKey);
console.log('Mode:', data.mode);
console.log('Do Not Sell state:', data.donotsellState);
if (data.donotsellState) {
console.log('User opted out of data selling');
// Track opt-out
analytics.track('ccpa_donotsell_enabled');
} else {
console.log('User allows data selling');
// Track opt-in
analytics.track('ccpa_donotsell_disabled');
}
});Use Cases:
- CCPA compliance tracking
- Update backend systems
- Display confirmation messages
- Analytics tracking
Custom DOM Events
All events are also dispatched as custom DOM events with the consentpro: prefix.
Listening to DOM Events
document.addEventListener('consentpro:consent-updated', (event) => {
const data = event.detail;
console.log('Consent updated via DOM event:', data);
});
document.addEventListener('consentpro:banner-shown', (event) => {
console.log('Banner shown:', event.detail);
});
document.addEventListener('consentpro:ready', (event) => {
console.log('Ready via DOM event:', event.detail);
});Benefits of DOM Events
- Works with any framework (React, Vue, Angular)
- Integrate with third-party scripts
- Use with legacy code
- Event delegation support
Example with React:
useEffect(() => {
const handleConsentUpdate = (event) => {
setConsent(event.detail.choices);
};
document.addEventListener('consentpro:consent-updated', handleConsentUpdate);
return () => {
document.removeEventListener('consentpro:consent-updated', handleConsentUpdate);
};
}, []);GTM Integration Events
Consent Pro automatically fires Google Tag Manager events for consent changes.
Google Consent Mode Mapping
Consent Pro automatically updates Google Consent Mode v2 based on cookie category consent. Here's how cookie categories map to consent modes:
| Cookie Category | Consent Modes Updated | Reject | Accept |
|---|---|---|---|
| Essential | security_storage | Granted | Always Granted |
| Analytics | analytics_storage | Denied | Granted |
| Marketing | ad_storagead_user_dataad_personalization | Denied | Granted |
| Personalization | functionality_storagepersonalization_storage | Denied | Granted |
Example: When a user grants Analytics consent, the following consent mode is updated:
gtag('consent', 'update', {
analytics_storage: 'granted',
});Example: When a user grants Marketing consent, multiple consent modes are updated:
gtag('consent', 'update', {
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
});Note: In opt-out and informational modes, all non-essential categories default to granted until the user opts out.
Category Activation Events
When a category is granted consent, a custom GTM event is fired:
analytics-activated- Analytics consent grantedmarketing-activated- Marketing consent grantedpersonalization-activated- Personalization consent grantedessential-activated- Essential consent granteduncategorized-activated- All non-essential consents granted
Example GTM Trigger:
// GTM Trigger: Custom Event
// Event name: analytics-activated
// This will fire when user grants analytics consent
console.log('Analytics activated - GTM tag will fire');fs-consent-consentModeUpdate Event
A special event fired whenever consent mode changes:
window.dataLayer.push({
event: 'fs-consent-consentModeUpdate',
});Use Case in GTM: Create a trigger on this event to fire tags when consent changes:
Trigger Type: Custom Event
Event name: fs-consent-consentModeUpdateListening to GTM Events
// Listen to dataLayer pushes
const originalPush = window.dataLayer.push;
window.dataLayer.push = function () {
const result = originalPush.apply(this, arguments);
const event = arguments[0];
if (event.event?.includes('activated')) {
console.log('Consent category activated:', event.event);
}
return result;
};Event Patterns
Wait for Initialization
Recommended: Using callbacks (automatically waits for initialization)
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
'consent',
async (instance) => {
// Code here runs automatically after initialization
const consent = instance.getConsent();
console.log('Ready with consent:', consent);
initializeServices();
},
]);Alternative: Using direct API access
// Pattern 1: Using ready event
window.FinsweetConsentProAPI.once('ready', () => {
const consent = window.FinsweetConsentProAPI.getConsent();
console.log('Ready with consent:', consent);
});
// Pattern 2: Check if already ready
if (window.FinsweetConsentProAPI?.isReady()) {
initializeServices();
} else {
window.FinsweetConsentProAPI.once('ready', initializeServices);
}Conditional Script Loading
Recommended: Using callbacks
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
'consent',
async (instance) => {
// Check existing consent on load
const currentConsent = instance.getConsent();
if (currentConsent?.choices.analytics && !window.analyticsLoaded) {
loadAnalyticsScript();
window.analyticsLoaded = true;
}
// Listen for consent updates
instance.on('consent-updated', (data) => {
const { choices } = data;
// Load scripts based on consent
if (choices.analytics && !window.analyticsLoaded) {
loadAnalyticsScript();
window.analyticsLoaded = true;
}
if (choices.marketing && !window.marketingLoaded) {
loadMarketingPixels();
window.marketingLoaded = true;
}
});
},
]);Alternative: Using direct API access
window.FinsweetConsentProAPI.on('consent-updated', (data) => {
const { choices } = data;
if (choices.analytics && !window.analyticsLoaded) {
loadAnalyticsScript();
window.analyticsLoaded = true;
}
if (choices.marketing && !window.marketingLoaded) {
loadMarketingPixels();
window.marketingLoaded = true;
}
});
window.FinsweetConsentProAPI.once('consent-loaded', (data) => {
const { choices } = data.consent;
if (choices.analytics) {
loadAnalyticsScript();
window.analyticsLoaded = true;
}
});Track All User Interactions
Recommended: Using callbacks
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
'consent',
async (instance) => {
const consentEvents = [
'banner-shown',
'banner-hidden',
'preferences-shown',
'preferences-hidden',
'consent-updated',
'donotsell-clicked',
];
consentEvents.forEach((event) => {
instance.on(event, (data) => {
// Send to analytics
analytics.track(`consent_${event}`, {
...data,
timestamp: Date.now(),
page: window.location.pathname,
});
});
});
// Or use subscribe for all events
instance.subscribe((event, data) => {
analytics.track(`consent_${event}`, {
...data,
timestamp: Date.now(),
page: window.location.pathname,
});
});
},
]);Debug All Events
if (window.location.search.includes('debug')) {
window.FinsweetConsentProAPI.subscribe((event, data) => {
console.group(`ConsentPro Event: ${event}`);
console.log('Data:', data);
console.log('Timestamp:', new Date().toISOString());
console.groupEnd();
});
}React Hook Pattern
Recommended: Using callback approach
import { useEffect, useState } from 'react';
function useConsentPro() {
const [consent, setConsent] = useState(null);
const [isReady, setIsReady] = useState(false);
useEffect(() => {
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
'consent',
async (instance) => {
setIsReady(true);
const current = instance.getConsent();
setConsent(current);
// Listen for updates
const unsubscribe = instance.on('consent-updated', (data) => {
setConsent(data.consent);
});
// Cleanup function
return () => {
unsubscribe();
};
},
]);
}, []);
return { consent, isReady };
}
// Usage
function MyComponent() {
const { consent, isReady } = useConsentPro();
if (!isReady) return <div>Loading consent...</div>;
return (
<div>
<p>Analytics: {consent?.choices.analytics ? 'Enabled' : 'Disabled'}</p>
<p>Marketing: {consent?.choices.marketing ? 'Enabled' : 'Disabled'}</p>
</div>
);
}Alternative: Using direct API access
import { useEffect, useState } from 'react';
function useConsentPro() {
const [consent, setConsent] = useState(null);
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const unsubReady = window.FinsweetConsentProAPI?.once('ready', () => {
setIsReady(true);
const current = window.FinsweetConsentProAPI.getConsent();
setConsent(current);
});
const unsubUpdate = window.FinsweetConsentProAPI?.on('consent-updated', (data) => {
setConsent(data.consent);
});
return () => {
unsubReady?.();
unsubUpdate?.();
};
}, []);
return { consent, isReady };
}Vue Composable Pattern
Recommended: Using callback approach
import { ref, onMounted } from 'vue';
export function useConsentPro() {
const consent = ref(null);
const isReady = ref(false);
onMounted(() => {
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
'consent',
async (instance) => {
isReady.value = true;
consent.value = instance.getConsent();
// Listen for updates
instance.on('consent-updated', (data) => {
consent.value = data.consent;
});
},
]);
});
return { consent, isReady };
}Alternative: Using direct API access
import { ref, onMounted, onUnmounted } from 'vue';
export function useConsentPro() {
const consent = ref(null);
const isReady = ref(false);
let unsubscribers = [];
onMounted(() => {
const api = window.FinsweetConsentProAPI;
unsubscribers.push(
api?.once('ready', () => {
isReady.value = true;
consent.value = api.getConsent();
})
);
unsubscribers.push(
api?.on('consent-updated', (data) => {
consent.value = data.consent;
})
);
});
onUnmounted(() => {
unsubscribers.forEach((unsub) => unsub?.());
});
return { consent, isReady };
}Next Steps
- API Reference - Complete API documentation