Skip to main content

Menu

Choose a theme and configure high-contrast mode. Preferences are saved in your browser only.

User Preferences

Theme

Pick a palette or follow your system preference.

High Contrast

Sharper text and borders. System follows your OS setting.

Configuration

Configuration Methods

Zest can be configured in three ways (in order of priority):

  1. Data attributes on the script tag - Highest priority
  2. window.ZestConfig object
  3. Default values - Lowest priority

When both sources set categories, Zest merges them per category, so data-hide-categories doesn’t discard labels defined in window.ZestConfig.

Using ZestConfig Object

<script>
  window.ZestConfig = {
    position: 'bottom-right',
    theme: 'auto',
    accentColor: '#0071e3',
    policyUrl: '/privacy-policy',
    mode: 'safe',
    expiration: 365
  };
</script>
<script src="https://cdn.jsdelivr.net/npm/@freshjuice/zest"></script>

Using Data Attributes

<script
  src="https://cdn.jsdelivr.net/npm/@freshjuice/zest"
  data-position="bottom-right"
  data-theme="auto"
  data-accent-color="#0071e3"
  data-policy-url="/privacy-policy"
  data-mode="safe"
  data-expiration="365"
  data-auto-init="true"
  data-show-widget="true"
  data-geo="on"
  data-geo-timeout="1500"
  data-geo-fallback="consent"
  data-branding="false"
  data-button-style="outline"
  data-button-layout="split"
  data-backdrop-blur="8"
  data-hard-wall="on"
  data-intercept-embeds="false"
  data-privacy-rewrite="false"
  data-hide-categories="analytics,marketing"
  data-privacy-url="/privacy"
  data-imprint-url="/imprint"
  data-consent-mode-google
  data-consent-mode-microsoft
></script>

data-mode works as of 2.8.0 — before that it was silently ignored.

Complete Options Reference

Every option, type, allowed value, and default is also published as a JSON Schema: zest.config.schema.json. Point your editor at it for validation and autocompletion of window.ZestConfig, or feed it to any tool that understands JSON Schema.

UI Options

Option Type Default Description
position string 'bottom' Banner position: 'bottom', 'bottom-left', 'bottom-right', 'top', 'top-left', 'top-right', 'center' (v2.6.0+)
theme string 'auto' Color theme: 'light', 'dark', 'auto' (follows system preference)
accentColor string '#0071e3' Primary button color. Validated — hex, named, rgb(), rgba(), hsl(), hsla()
showWidget boolean true Show floating widget after consent is given
branding boolean true Show a small “Powered by Zest” attribution link on the banner and settings modal. Set false (or data-branding="false") to remove it. Full build only (v2.4.0+)
buttonStyle string 'fill' Button style: 'fill' (solid) or 'outline' (accent-bordered transparent). Also accepted via data-button-style (v2.5.0+)
buttonLayout string 'row' Button arrangement: 'row' (all in one line), 'split' (settings on left, accept+reject on right), 'split-modern' (settings as primary, accept+reject as secondary). Also accepted via data-button-layout (v2.6.0+)
backdropBlur number 0 Blur radius in pixels for the page content behind the modal and hard wall overlay. 0 disables. Also accepted via data-backdrop-blur (v2.6.0+)
hardWall boolean false Full-viewport overlay behind the banner that blocks all page interaction until the visitor accepts or rejects. The banner becomes aria-modal when active. Also accepted via data-hard-wall (v2.6.0+)

Behavior Options

Option Type Default Description
autoInit boolean true Automatically initialize on page load
expiration number 365 Consent cookie expiration in days (1-365). See How long consent lasts for the Safari cap
mode string 'safe' Script blocking mode (see below)
privacyRewrite boolean true Rewrite gated YouTube embeds to youtube-nocookie.com and Vimeo players with dnt=1 when activated. Use false or data-privacy-rewrite="false" to keep original URLs (v3.0.0+)
blockedDomains array [] Additional domains to block
allowedDomains string[] [] Domains that bypass all blocking (incl. subdomains). cookiezest.com always allowed
Option Type Default Description
consentModeGoogle boolean false Enable Google Consent Mode v2 integration
consentModeMicrosoft boolean false Enable Microsoft UET Consent Mode integration

When enabled, Zest automatically pushes a 'default' denied state on initialization and sends 'update' signals on every consent change. See Consent Mode examples for usage.

Privacy Options

Option Type Default Description
respectDNT boolean true Respect Do Not Track and Global Privacy Control signals
dntBehavior string 'reject' DNT behavior: 'reject', 'preselect', 'ignore'
geo boolean | object null Opt-in geo / jurisdiction gating. true uses the hosted gateway; an object accepts provider, endpoint, resolver, decide, timeout, fallback. data-geo="on" is the attribute equivalent (v2.4.0+)
Option Type Default Description
policyUrl string null URL to privacy policy page. Validated — allowlist: http:, https:, mailto:, tel:, or relative
imprintUrl string null URL to imprint/legal page. Same validation as policyUrl

Language Options

Option Type Default Description
lang string 'auto' Language code or 'auto' for detection

Available languages: en, de, es, fr, it, pt, nl, pl, uk, ru, ja, zh

Advanced Options

Option Type Default Description
customStyles string '' Custom CSS to inject into Shadow DOM. Sanitized (20 KB cap; strips @import, @charset, expression(), -moz-binding, external url(), accept/reject-button selectors)
labels object {} Custom UI text labels, including labels.embed.title, .text (%category% inserts the localized category label), and .load for the embed overlay (v3.0.0+)
categories object {} Per-category overrides. Set hidden: true to remove a category from the settings modal — hidden categories are forced to false (rejected). Essential cannot be hidden. Also accepted via data-hide-categories="analytics,marketing" (v2.6.0+)
callbacks object {} Lifecycle callbacks. Exceptions are logged and swallowed
intercept object { cookies: true, storage: true, scripts: true, network: true, embeds: true } Disable interceptor channels individually. scripts controls script, link, image, and iframe element interception; network controls fetch, XHR, and beacon interception; embeds gates third-party iframes (set data-intercept-embeds="false" to disable). Channels since v2.2.0; network v2.3.0; embeds v3.0.0. See script blocking.
essentialKeys array [] Exact storage / cookie names to treat as strictly-necessary. Each is anchored as ^name$ and appended to the essential category — built-in essential patterns stay intact (v2.2.0+)
essentialPatterns array [] Regex source strings appended to the essential category. Validated via safeRegExp; 500-char cap (v2.2.0+)
patterns object {} Custom regex patterns for cookie categorization. Replaces the patterns for any category passed in. Prefer essentialKeys / essentialPatterns when you only want to add to the essential category. 500-char cap; catastrophic-backtracking patterns rejected

Headless integration example

When you bring your own UI and want Zest only for the consent state machine:

import Zest from '@freshjuice/zest/headless';

Zest.init({
  // Disable script blocking — you handle gating yourself
  intercept: { scripts: false },

  // Tell Zest your app's storage keys are strictly-necessary,
  // so the storage interceptor doesn't classify them as marketing
  // and block the user's explicit setItem() calls.
  essentialKeys: ['my_app_settings', 'theme_pref'],

  respectDNT: true,
  expiration: 365
});

Zest stores the visitor’s choice in a single first-party cookie so it survives page reloads and works with any backend. Know its shape, you will need it for your privacy policy, your CDN rules, and any server-side compliance logging.

Name zest_consent
Value URL-encoded JSON: { "version": "1.0", "timestamp": 1730000000000, "categories": { "essential": true, "functional": false, "analytics": false, "marketing": false } }
Attributes path=/, SameSite=Lax, Secure on HTTPS sites, expires per expiration
Deleted Zest.reset() expires it immediately

Your backend can read it server-side on every request, which is the reliable way to build a consent audit log; Zest.getConsentProof() returns the same payload from JS.

The Safari caveat: the expiration setting is the intended lifetime, not a guarantee: Zest sets the cookie from JavaScript, and Safari’s Intelligent Tracking Prevention caps all script-set cookies at 7 days. So every Safari visitor gets re-prompted at most a week after their last visit, no matter what you set. Chrome, Firefox, and Edge honor the full expiration. If week-long consent on Safari is a problem for your compliance model, mirror the cookie server-side (or use a Set-Cookie header from your backend) and re-issue it with a longer lifetime: a cookie set by the server is not capped.

Blocking Modes

Zest supports four script blocking modes:

Mode Description
manual Only blocks scripts with explicit data-consent-category attribute
safe Manual + known major trackers (Google Analytics, Facebook, GTM)
strict Safe + extended trackers (Hotjar, Mixpanel, Segment, etc.)
doomsday Block ALL third-party scripts
window.ZestConfig = {
  mode: 'safe' // Recommended for most sites
};

DNT Behavior Options

When respectDNT is true, the dntBehavior option controls what happens:

Behavior Description
'reject' Auto-reject all non-essential cookies, don’t show banner
'preselect' Show banner with non-essential categories unchecked
'ignore' Ignore DNT/GPC signals completely

If you use geo gating, the two features interact: see DNT, GPC and Geo for which one wins and when.

Custom Blocked Domains

Add additional domains to block:

window.ZestConfig = {
  mode: 'safe',
  blockedDomains: [
    'custom-tracker.com',
    { domain: 'analytics-service.com', category: 'analytics' }
  ]
};

Custom Allowed Domains (v2.7.0+)

Domains in allowedDomains bypass all blocking — mode, blockedDomains, and doomsday third-party blocking. Subdomains included ('example.com' matches 'api.example.com'). cookiezest.com is always allowed and doesn’t need to be listed.

window.ZestConfig = {
  mode: 'doomsday',
  allowedDomains: ['my-cdn.com', 'trusted-analytics.com']
};

Custom Labels

Override any UI text:

window.ZestConfig = {
  labels: {
    banner: {
      title: 'Cookie Settings',
      description: 'We use cookies to improve your experience.',
      acceptAll: 'Accept All',
      rejectAll: 'Reject All',
      settings: 'Customize'
    },
    modal: {
      title: 'Privacy Preferences',
      save: 'Save Preferences',
      acceptAll: 'Accept All',
      rejectAll: 'Reject All'
    },
    widget: {
      label: 'Cookie Settings'
    },
    notice: {
      title: 'Your Privacy Choices',
      optOut: 'Do Not Sell or Share My Personal Information',
      dismiss: 'Dismiss'
    },
    embed: {
      title: 'External content is blocked',
      text: 'Content in the %category% category does not load until you consent.',
      load: 'Load content'
    }
  }
};

The notice labels apply to the “Do Not Sell” notice shown for the geo 'notice' action (v2.4.0+).

Callbacks

React to consent lifecycle events:

window.ZestConfig = {
  callbacks: {
    onReady: (consent) => {
      console.log('Zest initialized:', consent);
    },
    onAccept: (consent) => {
      console.log('User accepted:', consent);
    },
    onReject: () => {
      console.log('User rejected all');
    },
    onChange: (consent) => {
      console.log('Consent changed:', consent);
    },
    onGeo: (action, verdict) => {
      // v2.4.0+ — fires once geo resolution completes (when geo is enabled)
      console.log('Geo action:', action, verdict);
    }
  }
};

Full Example

window.ZestConfig = {
  // UI
  position: 'bottom-right',
  theme: 'auto',
  accentColor: '#0071e3',
  showWidget: true,
  buttonStyle: 'fill',       // 'fill' or 'outline' (v2.5.0+)
  buttonLayout: 'row',       // 'row', 'split', 'split-modern' (v2.6.0+)
  backdropBlur: 0,           // blur radius in px, 0 disables (v2.6.0+)
  hardWall: false,           // block page until decided (v2.6.0+)

  // Behavior
  autoInit: true,
  expiration: 365,
  mode: 'safe',
  privacyRewrite: true,     // privacy-enhanced video URLs (v3.0.0+)
  intercept: { embeds: true }, // gate third-party iframes (v3.0.0+)

  // Consent Mode
  consentModeGoogle: true,
  consentModeMicrosoft: false,

  // Privacy
  respectDNT: true,
  dntBehavior: 'reject',
  geo: true, // opt-in jurisdiction gating (v2.4.0+)

  // Links
  policyUrl: '/privacy-policy',
  imprintUrl: '/legal',

  // Language
  lang: 'auto',

  // Categories — hide unused categories from the modal (v2.6.0+)
  categories: {
    marketing: { hidden: true }
  },

  // Callbacks
  callbacks: {
    onAccept: (consent) => {
      if (consent.analytics) {
        // Initialize analytics
      }
    }
  }
};