Consentify Blog

How to Add Cookie Consent to Next.js: The Simple Way

TL;DR Most Next.js consent guides show 50+ lines of boilerplate: useState, useEffect, gtag defaults, route listeners. There's a simpler approach: one script tag in layout.tsx, tracking configured in a dashboard. Same idea as GTM — keep tags out of your codebase.

Most guides for cookie consent in Next.js show you the same thing: a useState hook for consent state, a useEffect that reads localStorage, a custom banner component, a gtag('consent', 'default', { analytics_storage: 'denied' }) block, and then more code to update it when the user accepts. That's 50 to 100 lines of boilerplate before you've styled anything, and every new tracking script you add means touching the codebase again.

There's a simpler approach. One script tag in layout.tsx. Tracking configured in a dashboard. No consent boilerplate in your code at all.

Why Cookie Consent in Next.js Is Normally Complicated

Next.js adds specific challenges that plain HTML sites don't have. The App Router splits rendering between server and client components, which means any code that reads localStorage or calls window has to live in a 'use client' component. Route changes in Next.js don't trigger a full page reload, so consent logic needs to re-evaluate on navigation events, not just on initial load. And next/script has four loading strategies — pick the wrong one and your consent banner loads after your tracking scripts already fired.

The standard DIY approach handles all of this in code. You end up with consent state management, a banner component, route change listeners, and per-integration blocking logic all wired together manually. It works, but it's a lot of code to write, test, and maintain.

The Wrong Way: Tracking Scripts in Your Codebase

The most common pattern in Next.js consent guides looks like this: add GA4 directly to layout.tsx, set analytics_storage to 'denied' by default, build a banner component that calls gtag('consent', 'update', ...) when the user accepts, and store the result in localStorage.

This works for GA4. But what happens when you need to add Meta Pixel? Now you repeat the pattern for a second script. TikTok Pixel? A third. Each new integration means new code, new testing, and another thing that can break when the vendor updates their snippet. You're building a mini tag manager inside your Next.js app, and you'll be maintaining it indefinitely.

The Better Approach: One Script Tag, Everything in the Dashboard

The idea is the same as Google Tag Manager, but for consent management. Instead of putting tracking scripts in your codebase, you configure them in a dashboard. The consent tool handles blocking, injection after consent, and Consent Mode v2 signals. Your codebase stays clean.

With Consentify, the entire Next.js integration is one line in your root layout.tsx:

import Script from 'next/script'

const apiUrl =
  process.env.NODE_ENV === 'development'
    ? '/api/gateway?token=YOUR_TOKEN'
    : 'https://www.consentify.app/api/gateway?token=YOUR_TOKEN'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Script async src={apiUrl} />
      </body>
    </html>
  )
}

That's it. No 'use client' directive needed. No consent state. No useEffect. No banner component to build or style. The script handles all of that itself.

How It Handles the Next.js-Specific Problems

Consentify's script uses a Shadow DOM for the banner, so it stays isolated from your Next.js app's CSS and doesn't conflict with Tailwind or any component library you're using. It intercepts the native History API to detect route changes, which means consent state re-evaluates correctly on Next.js navigation without any additional code on your side. The script loads asynchronously and is served from an edge network, so it doesn't block your page render or affect your Core Web Vitals.

For the App Router specifically: the script goes in layout.tsx at the root level, not inside a 'use client' component. Next.js recommends using next/script with async for third-party scripts that don't need precise loading order control, which is exactly what this is. The consent banner needs to appear before tracking fires, and the script is designed to initialize before the integrations it manages.

Adding and Removing Tracking Scripts

This is where the GTM analogy is most useful. When a client wants to add LinkedIn Insight Tag, you don't touch the codebase. You log into the Consentify dashboard, add the integration, paste the pixel ID, and publish. The script cache invalidates and the new integration is live within minutes. When a client drops a tracking tool, you remove it from the dashboard. No deployment, no code review, no risk of accidentally breaking something else in the layout.

Consentify currently supports Google Analytics 4, Google Tag Manager, Meta Pixel, TikTok Pixel, LinkedIn Insight Tag, Snapchat Pixel, PostHog, HubSpot, Intercom, and more. Custom script integrations are also available on Pro plans for anything not in the pre-built list. See the full setup guide for the complete walkthrough.

What About Consent Mode v2?

If you're running Google Ads or GA4, Google Consent Mode v2 has been required for EEA traffic since March 2024. The standard DIY approach involves initializing GA4 with all consent parameters set to 'denied' and then updating them on user action. Consentify handles this automatically when you configure GA4 as an integration in the dashboard. You don't write any gtag('consent', ...) calls in your code. The consent signals are passed correctly without any boilerplate.

What You Still Need to Do

Adding the script tag is step one, not the whole setup. In the dashboard you need to configure your banner design, add your integrations, and add a revoke button somewhere on your site. GDPR requires that visitors can change their consent after the first visit, so add an element with the ID revoke-consent-btn in your footer or on your privacy policy page. Consentify attaches the consent panel to it automatically — no JavaScript needed.

<!-- In your footer component -->
<button id="revoke-consent-btn">Cookie preferences</button>

That single element, combined with the script tag in layout.tsx, is the complete Next.js implementation. Everything else lives in the dashboard.

When the DIY Approach Makes More Sense

If you have very specific requirements — a fully custom banner design that lives inside your React component tree, server-side consent checking for personalized content, or integration with a custom state management system — building your own consent flow in code gives you more control. The trade-off is the maintenance overhead described above. For most Next.js projects that just need compliant consent for a standard set of analytics and marketing tools, the one-script approach is faster to ship and easier to hand off to a client or another developer.

Ready to get started? Try Consentify free — one domain, no time limit.

Start your cookie banner for free

Get your own customizable, GDPR-ready banner in minutes with Consentify.

Get Started Free

Frequently asked questions

Where do I put the Consentify script tag in a Next.js App Router project?

Add it to your root layout.tsx file using the next/script component with the async attribute. Place it inside the body element. No 'use client' directive is needed — the script tag itself is a server component-compatible element.

Do I need to set gtag consent defaults to 'denied' in my Next.js code?

Not when using Consentify. Tracking scripts are configured in the Consentify dashboard, not in your codebase. Consentify handles blocking those scripts before consent and passing the correct Consent Mode v2 signals to GA4 automatically. You don't write any gtag consent code directly.

How does Consentify handle Next.js route changes?

Consentify's script intercepts the native History API pushState and replaceState events. This means it detects client-side navigation in Next.js and re-evaluates consent state on each route change without any additional code in your project.

Will the consent banner conflict with Tailwind CSS or my component library?

No. Consentify renders the banner inside a Shadow DOM, which keeps it isolated from your application's stylesheets. Tailwind classes, CSS Modules, styled-components, and global CSS won't affect the banner, and the banner's styles won't bleed into your layout.

Can I add new tracking integrations without redeploying my Next.js app?

Yes. Integrations are configured in the Consentify dashboard. When you add or remove a tracking tool, the script cache updates automatically. No code changes, no deployment, and no changes to layout.tsx required.

Written by Consentify
Helping you stay GDPR compliant, one banner at a time.