Consent banner visible on first load with separate toggles
Why it matters
Under GDPR Art. 7 and ePrivacy Art. 5(3), loading analytics or marketing cookies before a user grants consent is unlawful. Without a properly structured banner, you are collecting data that regulators can treat as illegally obtained, exposing the business to fines up to €20M or 4% of annual global turnover. Beyond fines, pre-ticked boxes or absent opt-in controls constitute consent theater — any downstream marketing use built on that data is also non-compliant under CCPA §1798.100. ISO-27001:2022 A.5.34 requires organizations to identify and fulfill applicable privacy obligations; a missing or broken consent banner is a direct gap against that control.
Severity rationale
Critical because absent or pre-ticked consent exposes every visitor's data to unauthorized collection, and regulators treat each affected user as a separate violation.
Remediation
Add a consent-gated analytics wrapper so non-essential scripts only initialize after the user accepts. Mount the banner in your root layout and store the result in localStorage before any tracker fires.
// components/CookieConsent.tsx
'use client'
import { useState, useEffect } from 'react'
type ConsentState = { analytics: boolean; marketing: boolean }
export function CookieConsent() {
const [show, setShow] = useState(false)
const [consent, setConsent] = useState<ConsentState>({ analytics: false, marketing: false })
useEffect(() => {
if (!localStorage.getItem('cookie_consent')) setShow(true)
}, [])
function save(state: ConsentState) {
localStorage.setItem('cookie_consent', JSON.stringify(state))
setShow(false)
if (state.analytics) window.gtag?.('consent', 'update', { analytics_storage: 'granted' })
}
if (!show) return null
return (
<div className="fixed bottom-0 left-0 right-0 p-4 bg-white border-t shadow-lg">
<p>We use cookies. Essential cookies are always active.</p>
<label>
<input type="checkbox" checked={consent.analytics}
onChange={e => setConsent(c => ({ ...c, analytics: e.target.checked }))} />
Analytics
</label>
<button onClick={() => save({ analytics: false, marketing: false })}>Reject all</button>
<button onClick={() => save({ analytics: true, marketing: true })}>Accept all</button>
<button onClick={() => save(consent)}>Save preferences</button>
</div>
)
}
All non-essential categories must default to unchecked. Wire the saved state to your analytics initialization before any <Script> or gtag call fires.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Data Protection Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- gdpr · Art. 7 — Conditions for consent
- gdpr · Art. 13 — Information to be provided where personal data are collected from the data subject
- eprivacy · Art. 5(3) — Confidentiality of communications; cookie consent requirement
- ccpa · §1798.100 — Consumer right to know about personal information collected
- iso-27001:2022 · A.5.34 — Privacy and protection of PII
Taxons
History
- 2026-04-18·v1.0.0·Initial import from data-protection·automated