Free20 minutesintermediate

Cookie Consent Implementation

Implement cookie consent banners and integrate with Zenovay for compliant analytics tracking.

cookiesconsentgdprbannercompliance
Last updated: January 15, 2025

Learn how to implement cookie consent and integrate it with Zenovay analytics for privacy compliance.

ModeCookies UsedConsent Needed
Privacy ModeNoneNo
Standard ModeSession cookieMay need
Extended ModePersistent cookieYes

Consent typically required when:

  • Using persistent cookies
  • Tracking across sessions
  • Identifying returning visitors
  • Collecting personal data

Consent may not be required when:

  • Using privacy mode (no cookies)
  • Strictly necessary functionality
  • Anonymous aggregate statistics

Privacy Mode (No Cookies)

Enable cookieless tracking by turning on Privacy Mode in the dashboard:

  1. Go to SettingsPrivacy
  2. Toggle Privacy Mode ON
  3. Save changes

Your script tag stays the same:

<script
  defer
  data-tracking-code="YOUR_TRACKING_CODE"
  src="https://api.zenovay.com/z.js"
></script>

Privacy mode:

  • Uses no cookies
  • No persistent identifiers
  • GDPR/CCPA compliant without consent
  • Slightly less accurate returning visitor data

Conditional Loading

// Check consent before loading
function loadZenovayIfConsented() {
  const consent = localStorage.getItem('analytics_consent');

  if (consent === 'granted') {
    loadZenovay();
  } else if (consent === 'denied') {
    // Don't load analytics
  } else {
    showConsentBanner();
  }
}

function loadZenovay() {
  const script = document.createElement('script');
  script.src = 'https://api.zenovay.com/z.js';
  script.setAttribute('data-tracking-code', 'YOUR_TRACKING_CODE');
  script.defer = true;
  document.head.appendChild(script);
}

// Run on page load
loadZenovayIfConsented();
<div id="cookie-banner" style="display: none;">
  <div class="cookie-content">
    <p>We use analytics to improve your experience.</p>
    <button onclick="acceptCookies()">Accept</button>
    <button onclick="declineCookies()">Decline</button>
    <a href="/privacy">Learn more</a>
  </div>
</div>

<script>
function showConsentBanner() {
  document.getElementById('cookie-banner').style.display = 'block';
}

function acceptCookies() {
  localStorage.setItem('analytics_consent', 'granted');
  document.getElementById('cookie-banner').style.display = 'none';
  loadZenovay();
}

function declineCookies() {
  localStorage.setItem('analytics_consent', 'denied');
  document.getElementById('cookie-banner').style.display = 'none';
  // Optionally load in privacy mode
  loadZenovayPrivacyMode();
}

function loadZenovayPrivacyMode() {
  // Privacy mode is configured in dashboard Settings → Privacy.
  // When enabled there, the tracker automatically uses cookieless mode.
  const script = document.createElement('script');
  script.src = 'https://api.zenovay.com/z.js';
  script.setAttribute('data-tracking-code', 'YOUR_TRACKING_CODE');
  script.defer = true;
  document.head.appendChild(script);
}
</script>

Cookiebot Integration

// Wait for Cookiebot consent
window.addEventListener('CookiebotOnAccept', function() {
  if (Cookiebot.consent.statistics) {
    loadZenovay();
  }
});

// Handle consent changes
window.addEventListener('CookiebotOnDecline', function() {
  // Consent withdrawn - stop tracking
  if (window.zenovay) {
    window.zenovay('disable');
  }
});

Cookiebot Configuration:

  1. Add Zenovay to "Statistics" category
  2. Set cookie name: zenovay_session
  3. Set provider: zenovay.com
  4. Set type: HTTP
  5. Set expiry: Session or 1 year

OneTrust Integration

// OneTrust consent callback
function OptanonWrapper() {
  if (OnetrustActiveGroups.includes('C0002')) {
    // Performance cookies consented
    loadZenovay();
  } else {
    // Load privacy mode
    loadZenovayPrivacyMode();
  }
}

OneTrust Categories:

  • C0001: Strictly Necessary
  • C0002: Performance (Zenovay)
  • C0003: Functional
  • C0004: Targeting

Osano Integration

// Osano consent callback
window.Osano.cm.addEventListener('osano-cm-consent-changed', function(event) {
  if (event.ANALYTICS === 'ACCEPT') {
    loadZenovay();
  }
});

Termly Integration

// Termly consent callback
window.addEventListener('termly-consent-preferences-updated', function(e) {
  if (e.detail.analytics) {
    loadZenovay();
  }
});

Integration

// Initialize Google Consent Mode
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}

gtag('consent', 'default', {
  'analytics_storage': 'denied'
});

// Update on consent
function updateConsent(granted) {
  gtag('consent', 'update', {
    'analytics_storage': granted ? 'granted' : 'denied'
  });

  if (granted) {
    loadZenovay();
  }
}

With Zenovay

Use the conditional loading approach shown above to only load Zenovay after consent is granted through Google Consent Mode:

// Load Zenovay only after analytics consent is granted
function updateConsent(granted) {
  gtag('consent', 'update', {
    'analytics_storage': granted ? 'granted' : 'denied'
  });

  if (granted) {
    const script = document.createElement('script');
    script.src = 'https://api.zenovay.com/z.js';
    script.setAttribute('data-tracking-code', 'YOUR_TRACKING_CODE');
    script.defer = true;
    document.head.appendChild(script);
  }
}

React Implementation

// ConsentContext.js
import { createContext, useContext, useState, useEffect } from 'react';

const ConsentContext = createContext();

export function ConsentProvider({ children }) {
  const [consent, setConsent] = useState(() => {
    return localStorage.getItem('analytics_consent') || 'pending';
  });

  const grantConsent = () => {
    localStorage.setItem('analytics_consent', 'granted');
    setConsent('granted');
  };

  const denyConsent = () => {
    localStorage.setItem('analytics_consent', 'denied');
    setConsent('denied');
  };

  return (
    <ConsentContext.Provider value={{ consent, grantConsent, denyConsent }}>
      {children}
    </ConsentContext.Provider>
  );
}

export const useConsent = () => useContext(ConsentContext);
// CookieBanner.js
import { useConsent } from './ConsentContext';

export function CookieBanner() {
  const { consent, grantConsent, denyConsent } = useConsent();

  if (consent !== 'pending') return null;

  return (
    <div className="cookie-banner">
      <p>We use cookies to analyze website traffic.</p>
      <button onClick={grantConsent}>Accept</button>
      <button onClick={denyConsent}>Decline</button>
    </div>
  );
}

Conditional Analytics

// Analytics.js
import { useEffect } from 'react';
import { useConsent } from './ConsentContext';

export function Analytics({ websiteId }) {
  const { consent } = useConsent();

  useEffect(() => {
    if (consent === 'granted') {
      const script = document.createElement('script');
      script.src = 'https://api.zenovay.com/z.js';
      script.setAttribute('data-tracking-code', websiteId);
      script.defer = true;
      document.head.appendChild(script);
    } else if (consent === 'denied') {
      // Don't load analytics when consent denied.
      // If you still want minimal tracking, enable Privacy Mode
      // in dashboard Settings → Privacy instead.
    }
  }, [consent, websiteId]);

  return null;
}

Vue.js Implementation

<!-- CookieConsent.vue -->
<template>
  <div v-if="showBanner" class="cookie-banner">
    <p>We use cookies for analytics.</p>
    <button @click="accept">Accept</button>
    <button @click="decline">Decline</button>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const showBanner = ref(false);

onMounted(() => {
  const consent = localStorage.getItem('analytics_consent');
  if (!consent) {
    showBanner.value = true;
  } else if (consent === 'granted') {
    loadZenovay();
  }
});

function accept() {
  localStorage.setItem('analytics_consent', 'granted');
  showBanner.value = false;
  loadZenovay();
}

function decline() {
  localStorage.setItem('analytics_consent', 'denied');
  showBanner.value = false;
  loadZenovayPrivacyMode();
}

function loadZenovay() {
  const script = document.createElement('script');
  script.src = 'https://api.zenovay.com/z.js';
  script.setAttribute('data-tracking-code', 'YOUR_TRACKING_CODE');
  document.head.appendChild(script);
}

function loadZenovayPrivacyMode() {
  // Privacy mode is configured in dashboard Settings → Privacy.
  // When enabled, the tracker automatically uses cookieless mode.
  const script = document.createElement('script');
  script.src = 'https://api.zenovay.com/z.js';
  script.setAttribute('data-tracking-code', 'YOUR_TRACKING_CODE');
  document.head.appendChild(script);
}
</script>

Allow users to change preferences:

<div class="cookie-preferences">
  <h2>Cookie Preferences</h2>

  <div class="cookie-option">
    <label>
      <input type="checkbox" id="necessary" checked disabled>
      Necessary Cookies (Required)
    </label>
    <p>Essential for the website to function.</p>
  </div>

  <div class="cookie-option">
    <label>
      <input type="checkbox" id="analytics">
      Analytics Cookies
    </label>
    <p>Help us understand how visitors use our site.</p>
  </div>

  <button onclick="savePreferences()">Save Preferences</button>
</div>

<script>
// Load current preferences
document.getElementById('analytics').checked =
  localStorage.getItem('analytics_consent') === 'granted';

function savePreferences() {
  const analytics = document.getElementById('analytics').checked;
  localStorage.setItem('analytics_consent', analytics ? 'granted' : 'denied');

  // Reload to apply changes
  location.reload();
}
</script>

Track consent for compliance:

function logConsent(type, granted) {
  fetch('/api/consent-log', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      type: type,
      granted: granted,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent
    })
  });
}

function acceptCookies() {
  logConsent('analytics', true);
  localStorage.setItem('analytics_consent', 'granted');
  loadZenovay();
}

Best Practices

  • Clear language: Explain what cookies do
  • Equal options: Accept and decline equally prominent
  • No dark patterns: Don't trick users into accepting
  • Granular control: Let users choose categories
  • Easy withdrawal: Allow changing preferences

Technical

  • Don't load before consent: Only load tracking after consent
  • Respect withdrawal: Stop tracking when consent withdrawn
  • Remember preferences: Don't ask repeatedly
  • Test thoroughly: Verify consent flow works

Compliance

  • Document consent: Log when consent given/withdrawn
  • Regular review: Update as regulations change
  • Audit trail: Keep records for compliance

Troubleshooting

Tracking Still Active After Decline

Check:

  • Script not hardcoded in HTML
  • Consent check runs before any tracking
  • No caching issues

Verify:

  • localStorage not blocked
  • Cookie not expiring immediately
  • Same domain for all pages

Ensure:

  • Integration code correct
  • Platform fully loaded before check
  • Callback names match

Next Steps

Was this article helpful?