SanityNextJS
3 min read
30.12.2025

Building a GDPR-Compliant Cookie Consent System with Sanity CMS

Last updated: 30.12.2025

Cookies used as a metaphore
🎧

Audio Narration

Listen to this post

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.