Install PrivacyPill
One script, about 5.2 kB gzipped. It sets Consent Mode defaults, shows your banner, remembers the choice, releases blocked scripts and tells Google what was decided.
Quick start
- 1. Design your banner in the customizer and save it to your domain. You'll get a site key like
pp_3f9a1c…. - 2. Paste the embed as the first script inside
<head>:
<script src="https://privacy-pill-consent-studio.lovable.app/api/public/v1/pp.js?site=pp_your_site_key"></script>3. Open your site in a private window. The banner appears and Google tags start in denied mode until the visitor chooses. Design changes go live within a few minutes of saving — the script is cached briefly at the edge.
Placement & fail-safe
Load PrivacyPill synchronously (no async/defer) and before Google Tag Manager or gtag.js. Consent Mode defaults must exist before the first Google tag runs.
For extra safety, add this inline line directly above the embed. If our script is ever blocked, Google tags still start denied. PrivacyPill detects the existing default and won't push a second one.
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag('consent','default',{ad_storage:'denied',ad_user_data:'denied',ad_personalization:'denied',analytics_storage:'denied',functionality_storage:'denied',personalization_storage:'denied',security_storage:'granted',wait_for_update:500});</script>Platform guides
- 1Open the template that renders the <head> on every page.
- 2Paste the snippet as the very first element inside <head>, above Google Tag Manager, gtag.js or any analytics.
- 3Mark non-Google trackers with type="text/plain" and data-pp-category so they wait for consent.
- 4Deploy and open your site in a private window: the banner appears and Google tags start in denied mode.
<!-- PrivacyPill: keep this first in <head>, before Google Tag Manager or analytics -->
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag('consent','default',{ad_storage:'denied',ad_user_data:'denied',ad_personalization:'denied',analytics_storage:'denied',functionality_storage:'denied',personalization_storage:'denied',security_storage:'granted',wait_for_update:500});</script>
<script src="https://privacy-pill-consent-studio.lovable.app/api/public/v1/pp.js?site=pp_your_site_key"></script>More detail: Shopify guide · Webflow guide
Blocking scripts & iframes
Google tags respect Consent Mode on their own. Everything else — Meta Pixel, Hotjar, TikTok, chat widgets, video embeds — should wait. Change type to text/plain and add data-pp-category (analytics, marketing or functional). PrivacyPill turns them into real scripts the moment that category is allowed, including on returning visits.
<!-- Runs only after the visitor allows "analytics" -->
<script type="text/plain" data-pp-category="analytics"
src="https://static.hotjar.com/c/hotjar-1234567.js?sv=6"></script>
<!-- Inline scripts work too -->
<script type="text/plain" data-pp-category="marketing">
fbq('init', '000000000000000'); fbq('track', 'PageView');
</script>
<!-- Embeds: load the iframe only after consent -->
<iframe data-pp-category="functional"
data-pp-src="https://www.youtube-nocookie.com/embed/VIDEO_ID"></iframe>Need a script type other than JavaScript (e.g. module)? Add data-pp-type="module". When someone withdraws consent, PrivacyPill deletes first-party cookies you listed under the category's "cookies to delete" and can optionally reload the page.
Google Consent Mode v2
| Banner category | Consent Mode signals | Default |
|---|---|---|
| Strictly necessary | security_storage | granted |
| Analytics | analytics_storage | denied |
| Marketing & advertising | ad_storage, ad_user_data, ad_personalization | denied |
| Functional | functionality_storage, personalization_storage | denied |
On load, PrivacyPill pushes gtag('consent', 'default', …) with wait_for_update (500 ms by default), optionally ads_data_redaction and url_passthrough. If the visitor already chose, an update follows immediately. Build your own setup with the Consent Mode v2 generator.
Events & dataLayer
Every change is pushed to the dataLayer so you can trigger GTM tags on it:
dataLayer.push({
event: "privacypill_consent", // also: privacypill_ready, privacypill_show, privacypill_hide, privacypill_reset
privacypill: {
analytics: "granted",
marketing: "denied",
functional: "granted"
}
});The same moments fire DOM events on window: privacypill:ready, privacypill:consent, privacypill:show, privacypill:hide and privacypill:reset. event.detail contains consent, gcm, method (accept / reject / custom / api), id and decided.
JavaScript API
// Open the preferences panel (e.g. from a "Cookie settings" footer link)
PrivacyPill.open();
// Read the current choice
PrivacyPill.get(); // { necessary: true, analytics: true, marketing: false, functional: true }
PrivacyPill.allowed("marketing"); // false
// React to changes
PrivacyPill.on(function (e) {
if (e.type === "consent" && e.consent.analytics) startMyAnalytics();
});
// Or listen to the DOM event
window.addEventListener("privacypill:consent", function (e) {
console.log(e.detail.gcm); // { ad_storage: "denied", analytics_storage: "granted", … }
});
// Ask again (e.g. after you add a new tool)
PrivacyPill.reset();Also available: PrivacyPill.accept(), PrivacyPill.reject(), PrivacyPill.set({ analytics: true }) and PrivacyPill.show().
Languages
English, German, French, Dutch, Spanish and Italian are built in. With detection on, PrivacyPill uses your page's <html lang> first, then the visitor's browser languages, then your fallback language. Only the languages you enable are shipped in your script.
Geo-targeting (Pro)
With geo-targeting on, visitors from the EU/EEA, UK, Switzerland and California see the banner as usual. Visitors elsewhere get consent granted by default and no banner — but can still open the settings via the floating button or PrivacyPill.open(). When we can't determine a visitor's location, the banner is shown.
What we store
- In the visitor's browser: one first-party cookie,
pp_consent, containing a random consent ID, the choice, the banner version and a timestamp. It lasts as long as you configure (default 182 days), isSameSite=LaxandSecureon HTTPS. - On our side: a monthly pageview counter per site, and a proof-of-consent record (consent ID, choice, banner version, country). No IP addresses, no user agents.
Troubleshooting
The banner doesn't appear
Check the site key in the script URL, and that you're testing in a private window (a stored choice hides the banner). Use 'Check installation' on your site's dashboard page.
Google tags fire before consent
PrivacyPill must load before GTM/gtag.js and without async. Add the fail-safe line above it.
My changes don't show up
The script is cached at the edge for a few minutes. Hard-refresh after ~5 minutes.
A blocked script never runs
Make sure the category name is exactly analytics, marketing or functional, and the type is text/plain.