TLDR: Cookie consent doesn't have to tank your Lighthouse score. Here's how to add GDPR compliance in 60 seconds with a 1KB loader that sets Google Consent Mode v2 signals synchronously.
Read full summary
A developer-to-developer deep dive on cookie consent implementation. Covers why most CMPs hurt performance, how Google Consent Mode v2 works, and a solution that respects your bundle size.
*Summary by Claude AI*
## Cookie Consent for Modern Web Developers: Why We Rebuilt It From Scratch
*Originally posted on Dev.to*
Last month I ran a Lighthouse audit on a client site. Performance score: 68.
The culprit? Their cookie consent banner. Not the banner itself—the 147KB JavaScript bundle it loaded.
For comparison, React is 42KB. Their consent popup was loading 3.5x the size of React. To show a popup. And ask a yes/no question.
This is the state of cookie consent in 2024. Enterprise tools built for marketing teams, deployed on developer sites, destroying Core Web Vitals everywhere they go.
Let me show you a better way.
## The Real Problem: Timing
Here's what most developers don't know about Google Consent Mode.
When GTM loads, it checks consent state immediately. If your consent banner hasn't loaded yet—and it probably hasn't, because it's 100KB+ of JavaScript—GTM has no consent signals. It defaults to sending data.
This is what compliant looks like:
```javascript
// MUST run before GTM
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied'
});
```
And this is what most CMPs actually do:
```javascript
// Runs after DOMContentLoaded
// By then, GTM has already fired
gtag('consent', 'default', { ... }); // Too late
```
If your consent defaults don't run synchronously in the ``, they're decorative. GTM doesn't wait.
## The Technical Requirements
Here's what a proper consent implementation needs:
### 1. Synchronous Consent Defaults
Must run in `` before GTM. Can't be async. Can't wait for DOMContentLoaded.
### 2. Google Developer ID
Google has a CMP Partner Program. Registered developers get an ID that tells Google "these consent signals are trustworthy":
```javascript
gtag('set', 'developer_id.dODAxZj', true);
```
### 3. Google Consent Mode v2 Signals
Since March 2024, Google requires four specific signals:
| Signal | What It Controls |
|--------|------------------|
| `ad_storage` | Advertising cookies |
| `ad_user_data` | Sending user data to Google |
| `ad_personalization` | Personalized advertising |
| `analytics_storage` | Analytics cookies |
### 4. Third-Party Pixel Integration
Meta, TikTok, LinkedIn, Pinterest—they all have their own consent APIs:
```javascript
// Meta
fbq('consent', 'grant');
fbq('consent', 'revoke');
// TikTok
ttq.enableCookie();
ttq.disableCookie();
// LinkedIn: Nothing. Just block the script.
```
### 5. Script Blocking
Scripts marked with `type="text/plain"` need to be activated after consent:
```html
```
## What I Built
I got tired of explaining to clients why their Lighthouse score dropped after adding "just a cookie banner." So I built one that doesn't suck:
```html
```
### How It Works
**1KB synchronous loader:**
- Runs immediately in ``
- Sets consent defaults before GTM loads
- No blocking, no waiting
**Async widget:**
- Loads the banner UI separately
- Doesn't block rendering
- Zero CLS (positioned fixed from start)
### The API
```javascript
// Check consent state
const hasAnalytics = window.GetCookies.hasConsent('analytics');
const hasMarketing = window.GetCookies.hasConsent('marketing');
// Listen for changes
window.addEventListener('getcookies:consent', (e) => {
const { analytics, marketing, necessary } = e.detail;
if (marketing && !window.myPixelLoaded) {
loadMyPixel();
window.myPixelLoaded = true;
}
});
// Open preferences modal
window.GetCookies.showPreferences();
```
No callbacks. No jQuery dependencies. Just modern JavaScript.
## Framework Integration
### Next.js (App Router)
```tsx
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
```
### Remix
```tsx
// app/root.tsx
import { Links, Meta, Outlet, Scripts } from '@remix-run/react';
export default function App() {
return (
```
### Option 2: Event Listener
```javascript
window.addEventListener('getcookies:consent', (e) => {
if (e.detail.marketing) {
// Load Meta Pixel
loadMetaPixel();
}
if (e.detail.analytics) {
// Load GA4
loadGA4();
}
});
```
### Option 3: Polling (For Already-Loaded Scripts)
```javascript
// For scripts that load regardless but need consent gating
function initAnalytics() {
if (!window.GetCookies?.hasConsent('analytics')) {
return;
}
// Initialize analytics
gtag('config', 'G-XXXXXXXX');
}
```
## Free Tier
Because side projects shouldn't need a budget:
- 1 domain
- Unlimited page views
- All core features
- Google Consent Mode v2
- Third-party pixel integration
- No credit card required
## The Bottom Line
Cookie consent is a solved problem. The industry just solved it with enterprise budgets and marketing team requirements.
For developers who care about performance, who want clean APIs, and who don't want to read 47 pages of Google documentation—there's now a better option.
**The code:**
```html
```
**The result:**
- GDPR compliant
- Google Consent Mode v2 configured
- Third-party pixels handled
- Lighthouse score intact
60 seconds. 1KB. Done.
---
**Try it:** [getcookies.co](https://getcookies.co)
**Feedback welcome.** What features are missing? What integrations do you need? What did I get wrong?