Events & Callbacks

React to consent changes with Zest events and callbacks

Overview

Zest provides three ways to react to consent changes:

  1. DOM Events - Standard browser events you can listen to anywhere
  2. Zest.on() / Zest.once() subscribers (v2.0.0+) - Cleaner API with explicit unsubscribe
  3. Callbacks - Functions defined in your configuration

DOM Events

zest:ready

Fired when Zest has initialized and determined the current consent state.

document.addEventListener('zest:ready', (e) => {
  console.log('Zest initialized');
  console.log('Current consent:', e.detail.consent);
  // { essential: true, functional: false, analytics: false, marketing: false }
});

Fired when the user accepts cookies (either all or selected categories).

document.addEventListener('zest:consent', (e) => {
  console.log('User consented');
  console.log('Categories:', e.detail.consent);
  console.log('Previous state:', e.detail.previous);

  if (e.detail.consent.analytics) {
    initializeAnalytics();
  }

  if (e.detail.consent.marketing) {
    initializeMarketing();
  }
});

zest:reject

Fired when the user rejects all non-essential cookies.

document.addEventListener('zest:reject', (e) => {
  console.log('User rejected all');
  console.log('Consent state:', e.detail.consent);
  // { essential: true, functional: false, analytics: false, marketing: false }
});

zest:change

Fired on any consent change (accept, reject, or modification).

document.addEventListener('zest:change', (e) => {
  console.log('Consent changed');
  console.log('New state:', e.detail.consent);
  console.log('Previous state:', e.detail.previous);

  // Check what changed
  const prev = e.detail.previous;
  const curr = e.detail.consent;

  if (prev.analytics !== curr.analytics) {
    console.log('Analytics consent changed to:', curr.analytics);
  }
});

zest:show

Fired when the banner or modal is displayed.

document.addEventListener('zest:show', (e) => {
  console.log('UI shown:', e.detail.type); // 'banner' or 'modal'
});

zest:hide

Fired when the banner or modal is hidden.

document.addEventListener('zest:hide', (e) => {
  console.log('UI hidden:', e.detail.type); // 'banner' or 'modal'
});

Subscriber API

Since v2.0.0.

Zest.on() and Zest.once() are thin wrappers around the DOM events above that return an unsubscribe function and let you omit the zest: prefix. Handy when you want a clean teardown path (e.g. inside a SPA component).

Zest.on(event, callback)

const unsubscribe = Zest.on('consent', (consent) => {
  if (consent.analytics) initAnalytics();
});

// Later, stop listening
unsubscribe();

The event name accepts both forms:

Zest.on('change', handler);         // short form
Zest.on('zest:change', handler);    // full event name
Zest.on(Zest.EVENTS.CHANGE, handler); // via constants

Zest.once(event, callback)

Same signature, but the callback fires at most once and then auto-unsubscribes:

Zest.once('ready', (consent) => {
  console.log('Zest is ready, current consent:', consent);
});

Unsubscribing everywhere

Every subscriber — DOM event, on, once, callback — receives the same payload shape. Pick the style that fits your codebase; you don't have to be consistent.

// In a React component, on/off cleanly
useEffect(() => {
  const unsubscribe = Zest.on('change', (consent) => {
    setConsent(consent);
  });
  return unsubscribe;
}, []);

Configuration Callbacks

Define callbacks in your ZestConfig for a cleaner setup:

window.ZestConfig = {
  callbacks: {
    onReady: (consent) => {
      console.log('Ready with consent:', consent);
    },

    onAccept: (consent) => {
      console.log('Accepted:', consent);

      if (consent.analytics) {
        gtag('config', 'GA-XXXXX');
      }
    },

    onReject: () => {
      console.log('All rejected');
    },

    onChange: (consent) => {
      console.log('Changed to:', consent);
    }
  }
};

Callback Reference

Callback Parameters When Fired
onReady consent Zest initialized
onAccept consent User accepted (any categories)
onReject none User rejected all
onChange consent Any consent change

Practical Examples

Google Analytics Integration

document.addEventListener('zest:consent', (e) => {
  if (e.detail.consent.analytics) {
    // Load gtag.js dynamically
    const script = document.createElement('script');
    script.src = 'https://www.googletagmanager.com/gtag/js?id=GA-XXXXX';
    script.async = true;
    document.head.appendChild(script);

    script.onload = () => {
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', 'GA-XXXXX');
    };
  }
});

Facebook Pixel Integration

document.addEventListener('zest:consent', (e) => {
  if (e.detail.consent.marketing) {
    !function(f,b,e,v,n,t,s)
    {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
    n.callMethod.apply(n,arguments):n.queue.push(arguments)};
    if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
    n.queue=[];t=b.createElement(e);t.async=!0;
    t.src=v;s=b.getElementsByTagName(e)[0];
    s.parentNode.insertBefore(t,s)}(window, document,'script',
    'https://connect.facebook.net/en_US/fbevents.js');
    fbq('init', 'PIXEL_ID');
    fbq('track', 'PageView');
  }
});
class ConsentAwareAnalytics {
  constructor() {
    this.queue = [];
    this.initialized = false;

    document.addEventListener('zest:consent', (e) => {
      if (e.detail.consent.analytics) {
        this.initialize();
      }
    });

    // Check if already consented
    if (typeof Zest !== 'undefined' && Zest.hasConsent('analytics')) {
      this.initialize();
    }
  }

  initialize() {
    if (this.initialized) return;
    this.initialized = true;

    // Initialize your analytics here
    // ...

    // Process queued events
    this.queue.forEach(event => this.track(event.name, event.data));
    this.queue = [];
  }

  track(name, data) {
    if (!this.initialized) {
      this.queue.push({ name, data });
      return;
    }
    // Send to analytics
    gtag('event', name, data);
  }
}

const analytics = new ConsentAwareAnalytics();

// Safe to call anytime - will queue if no consent
analytics.track('button_click', { label: 'signup' });
<footer>
  <a href="#" onclick="Zest.showSettings(); return false;">
    Cookie Preferences
  </a>
</footer>
document.addEventListener('zest:consent', (e) => {
  const proof = Zest.getConsentProof();

  // Send to your server
  fetch('/api/consent-log', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      consent: proof,
      userAgent: navigator.userAgent,
      url: window.location.href
    })
  });
});