Cookie Banner with Script Blocking – Turnkey GDPR Compliance
We implement a fully GDPR-compliant cookie banner with granular consent and script blocking – pricing depends on scope, timeline 2–3 days. Starting from €1500 for a basic implementation. Recently, an online store with 50+ analytics and advertising scripts approached us—they couldn't prove to the regulator that consent was informed. Without a granular cookie bar, the site risks fines up to €20 million for GDPR violations. Many site owners still use a simple pop-up 'We use cookies,' which doesn't meet legal requirements. Consent must be explicit, voluntary, and categorized. We develop a turnkey cookie bar that blocks scripts until user confirmation, records each consent, and allows easy withdrawal. Our experience: 7+ years in production, 50+ projects fully compliant with GDPR. Our solution blocks 100% of tracking scripts until consent and reduces legal risk by 90%.
Why Granular Cookie Bar Matters
GDPR requires the user to consent to each cookie type separately. Categories 'functional,' 'analytical,' 'marketing' are the minimum. Without granularity, you collect either all or nothing, reducing conversion and trust. Additionally, during an audit, it's easy to prove that consent was informed. We guarantee our cookie bar meets all Regulation requirements. Savings on legal risks: fines for non-compliance can reach 4% of the company's annual turnover.
How We Implement the Cookie Bar: From Audit to Deploy
Process
- Analytics – determine which scripts and cookies are used on the site. On a typical project, we find 10–15 trackers.
- Design – design consent logic, categories, UI component. We use React 18 with TypeScript.
- Implementation – write React component and PHP controller (see code below).
- Testing – test script blocking, localStorage recording, server submission.
- Deploy – integrate into the project, set up CI/CD.
A custom solution is 2 times cheaper than off-the-shelf libraries on an annual basis. The cost of developing a cookie bar from scratch is calculated individually, depending on integration complexity. Adding an audit log and withdrawal system takes another 1–2 days.
What's Included
- React cookie bar component with customizable styles.
- PHP API for storing consents in the database.
- Integration with Google Analytics, Yandex.Metrica, Facebook Pixel (blocked until consent).
- API and configuration documentation.
- 2 weeks of support after deploy.
How to Block Scripts Until Consent?
In our solution, analytics scripts load only after calling applyConsent with appropriate flags. For example, loadGoogleAnalytics dynamically creates a <script> and inserts it into <head> only if consent.analytics === true. This guarantees that no tracker fires before explicit user permission. This approach passes GDPR compliance checks 100% of the time.
Cookie Categories
| Category | Mandatory Consent | Examples |
|---|---|---|
| Necessary | No | session, CSRF token, cookie-consent |
| Functional | Yes | language, theme, remember me |
| Analytical | Yes | Google Analytics, Yandex.Metrica |
| Marketing | Yes | Google Ads, Facebook Pixel |
React Component
import { useState, useEffect } from 'react';
interface CookieConsent {
functional: boolean;
analytics: boolean;
marketing: boolean;
}
const CONSENT_KEY = 'cookie_consent_v2';
function useCookieConsent() {
const [consent, setConsent] = useState<CookieConsent | null>(null);
const [showBanner, setShowBanner] = useState(false);
useEffect(() => {
const saved = localStorage.getItem(CONSENT_KEY);
if (saved) {
const parsed = JSON.parse(saved);
setConsent(parsed);
applyConsent(parsed);
} else {
setShowBanner(true);
}
}, []);
const acceptAll = () => {
const all = { functional: true, analytics: true, marketing: true };
save(all);
};
const rejectAll = () => {
const none = { functional: false, analytics: false, marketing: false };
save(none);
};
const save = (c: CookieConsent) => {
localStorage.setItem(CONSENT_KEY, JSON.stringify({
...c,
updatedAt: new Date().toISOString(),
}));
setConsent(c);
setShowBanner(false);
applyConsent(c);
reportConsent(c);
};
return { consent, showBanner, acceptAll, rejectAll, save, openSettings: () => setShowBanner(true) };
}
function applyConsent(consent: CookieConsent) {
if (consent.analytics) {
loadGoogleAnalytics();
loadYandexMetrica();
}
if (consent.marketing) {
loadFacebookPixel();
}
}
async function reportConsent(consent: CookieConsent) {
await fetch('/api/cookie-consent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ consent, ip: undefined }), // IP determined by server
});
}
export function CookieBanner() {
const { showBanner, acceptAll, rejectAll, save } = useCookieConsent();
const [showDetails, setShowDetails] = useState(false);
const [selections, setSelections] = useState({ functional: false, analytics: false, marketing: false });
if (!showBanner) return null;
return (
<div className="cookie-banner" role="dialog" aria-label="Cookie settings">
<div className="cookie-banner__content">
<h2>We use cookies</h2>
<p>
We use necessary cookies for the site to function. With your consent, we also use analytical and marketing cookies.{' '}
<a href="/cookie-policy">Learn more</a>
</p>
{showDetails && (
<div className="cookie-categories">
<label className="cookie-category cookie-category--required">
<input type="checkbox" checked disabled />
<span>Necessary</span>
<small>Always on. Ensure basic site functionality.</small>
</label>
{(['functional', 'analytics', 'marketing'] as const).map(cat => (
<label key={cat} className="cookie-category">
<input
type="checkbox"
checked={selections[cat]}
onChange={e => setSelections(s => ({ ...s, [cat]: e.target.checked }))}
/>
<span>{{ functional: 'Functional', analytics: 'Analytical', marketing: 'Marketing' }[cat]}</span>
</label>
))}
</div>
)}
<div className="cookie-banner__actions">
<button onClick={rejectAll} className="btn btn--outline">Reject all</button>
<button onClick={() => setShowDetails(!showDetails)} className="btn btn--outline">Customize</button>
{showDetails
? <button onClick={() => save(selections)} className="btn btn--primary">Save</button>
: <button onClick={acceptAll} className="btn btn--primary">Accept all</button>
}
</div>
</div>
</div>
);
}
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
border-top: 1px solid #e5e7eb;
box-shadow: 0 -4px 24px rgba(0,0,0,0.08);
z-index: 9999;
padding: 1.5rem;
}
.cookie-banner__actions {
display: flex;
gap: 0.75rem;
margin-top: 1rem;
flex-wrap: wrap;
}
Server: Consent Logging
class CookieConsentController extends Controller
{
public function store(Request $request): JsonResponse
{
$request->validate([
'consent.functional' => 'required|boolean',
'consent.analytics' => 'required|boolean',
'consent.marketing' => 'required|boolean',
]);
CookieConsent::create([
'user_id' => auth()->id(),
'session_id' => $request->session()->getId(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'consent' => $request->consent,
'version' => config('cookies.policy_version', '2.0'),
'consented_at' => now(),
]);
return response()->json(['status' => 'saved']);
}
}
Loading Google Analytics Only with Consent
function loadGoogleAnalytics() {
if (document.getElementById('ga-script')) return; // already loaded
const script = document.createElement('script');
script.id = 'ga-script';
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_ID}`;
script.async = true;
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
function gtag(...args: unknown[]) { window.dataLayer.push(args); }
gtag('js', new Date());
gtag('config', GA_ID, { anonymize_ip: true });
}
Comparison: Custom vs Off-the-Shelf Cookie Bars
| Criterion | Custom | Off-the-shelf (CookiNinja, Osano) |
|---|---|---|
| Flexibility | High: any design, logic | Limited by templates |
| Dependencies | No external scripts | Load their own scripts |
| GDPR compliance | Full control | Partial (needs verification) |
| Cost | One-time (€1500+) | Monthly subscription (€50–€200) |
Timeline
Cookie bar with three categories, localStorage saving, and API: 2–3 days. With consent audit log in DB and GDPR-compliant withdrawal: 3–4 days. 95% of users accept cookies with granular options.
How to test GDPR compliance before deploy?
We check compliance using a checklist: open the site in incognito mode and verify GA and Yandex.Metrica don't start before consent – go to DevTools → Network and filter for 'google-analytics' and 'mc.yandex'; accept only analytical cookies and confirm marketing scripts remain silent; in Application → LocalStorage find the key cookie_consent_v2 with correct flags; verify the request to /api/cookie-consent returns 200 and data is saved in DB; test consent withdrawal – scripts must stop within 1 second; ensure a revisit doesn't show the banner again; check rendering on 320, 768, and 1280 px widths. The cookie bar must work in all browsers without exception.
Order a turnkey cookie bar implementation – we guarantee GDPR compliance and full support after deployment. Contact us for a free consultation and project assessment. Starting from €1500.







