# Building a GDPR-Compliant Cookie Consent System with Sanity CMS

**URL:** https://andreskristensen.blog/post/gdpr-compliant-cookie-consent-sanity-cms

![Cookies used as a metaphore ](https://cdn.sanity.io/images/i6gaeymf/production/f06033669fd7f73988af6eb3675f1d7bfc9d9ea4-6000x3376.jpg)


---

**Summary:** The author decided to build a custom cookie consent solution for their blog to avoid the complexities and privacy issues of third-party libraries. Their solution is fully GDPR-compliant, privacy-first, and integrates with Sanity CMS. The custom implementation includes three main components: a Sanity CMS schema for managing consent content, a React Context for state management, and conditional loading of analytics based on user consent. The system emphasizes accessibility, server-side data fetching, and smart reload logic to ensure a seamless user experience. The cookie banner is designed to be editor-friendly, allowing non-technical users to update content without code changes. The result is a lightweight, under 5KB, GDPR-compliant system that respects user privacy by default, loads no third-party tracking libraries, and provides full transparency with a detailed policy page. Users can withdraw consent at any time, and the system is fully accessible, making it an ideal solution for personal blogs or small sites.

When I started thinking about adding analytics to my blog, I quickly realized that most cookie consent solutions felt like overkill. Third-party libraries added unnecessary weight, came with their own tracking scripts, and often lacked the flexibility I wanted. So I decided to build my own—fully GDPR-compliant, privacy-first, and powered by Sanity CMS.

## Why Build Your Own?

The problem with most cookie consent libraries is that they're designed for complex enterprise scenarios. For a personal blog that only uses privacy-respecting analytics (Vercel Analytics in my case), I needed something simpler:

- **No third-party scripts** - Why add another tracker to manage trackers?
- **Full control** - I wanted to understand exactly what was happening
- **Content management** - Legal text should be editable without code changes
- **Privacy by default** - No cookies until explicit consent

## The Architecture

My implementation has three main components:

### 1. Sanity CMS as the Single Source of Truth

I created a `cookieConsent` singleton schema in Sanity that stores:

- Banner text (what users see)
- Accept/Reject button labels
- Full cookie policy content (as Portable Text)
- Last updated timestamp

This means my content team (okay, just me) can update the consent text, policy details, or button labels directly in the Sanity Studio—no code deployment needed.

### 2. React Context for State Management

A simple `CookieConsentProvider` handles:

- Reading consent state from `localStorage`
- Providing accept/reject/withdraw methods
- Preventing hydration mismatches with an `isMounted` flag

The key insight here was to initialize state as `null` and read from `localStorage` in `useEffect` to avoid the flash-of-content issue during SSR.

### 3. Conditional Analytics Loading

The `ConditionalAnalytics` component only renders Vercel Analytics and SpeedInsights when `consent === 'accepted'`. If consent is rejected or not given, nothing loads. Simple as that.



## Key Implementation Details

### Accessibility First

The cookie banner includes proper ARIA attributes:

- `role="dialog"` and `aria-modal="true"` for screen readers
- `aria-label` attributes on all buttons
- Semantic HTML throughout

### Server-Side Data Fetching

Cookie consent sanity data is fetched server-side in the Next.js layout and passed as props to the banner component. This keeps the banner itself lightweight and ensures no client-side Sanity queries.

### Smart Reload Logic

When transitioning from "accepted" to "rejected," I reload the page to clear any loaded analytics scripts. But rejecting when you haven't accepted yet? No reload needed—nothing to clear.

### No Flash on Load

The banner checks `isMounted` before rendering, ensuring it only appears client-side after `localStorage` is read. This eliminates the annoying flash where the banner appears briefly even when consent was already given.

### Learn More Link

The banner includes a "Learn more" link to the full cookie policy page, rendered from Sanity's Portable Text. Users can make an informed decision without feeling pressured.

### Footer Management

A "Manage Cookie Preferences" button in the footer lets users withdraw consent at any time—a GDPR requirement that's often overlooked.



## Why This Approach Works

**Full Control**: I understand every line of code. No black boxes.

**Privacy-Focused**: No analytics load until the user explicitly accepts. No sneaky preconnections or preloads.

**Editor-Friendly**: Non-technical users can update the policy text, banner wording, or button labels through Sanity Studio.

**Performance**: No third-party library to download. The entire implementation is under 5KB.

## The Result

A fully GDPR-compliant cookie consent system that:

- ✅ Respects user privacy by default
- ✅ Loads no third-party tracking libraries
- ✅ Provides full transparency with a detailed policy page
- ✅ Allows users to withdraw consent anytime
- ✅ Is fully accessible
- ✅ Can be updated by content editors without code changes

Building your own cookie consent isn't complicated—it just requires thinking through the user experience and legal requirements. For a personal blog or small site, a custom solution often makes more sense than pulling in a heavy library.