How to add Cookie Consent to a Next.js 13 + 14 Site with Google Tag Manager

· 25 min read

Table of Contents

    Introduction (with Next 14 update)

    Implementing a robust cookie consent system is essential for any website. Cookies, small data files stored on a user's device, are commonly used for various functionalities like maintaining user sessions and tracking user behavior for analytics. However, to maintain user trust and comply with data privacy laws such as the General Data Protection Regulation (GDPR), it's crucial to request user consent before setting these cookies.

    Update 10/26 2023 - Next.js 14 Released

    Next.js 14 has been released, including Server Actions (Stable) and Partial Prerendering (Preview). The guidance in this post still applies.

    This article is a comprehensive guide to those who are looking to integrate a cookie consent solution into a Next.js 13 / 14 application, alongside Google Tag Manager (GTM). This guide is particularly useful for those working with the Next.js 13 / 14 framework, a popular choice for building efficient, scalable React applications.

    We'll walk you through the process of building a cookie consent banner, tailoring your consent approach to user preferences, and integrating your solution with GTM for comprehensive user data management.

    Steps to adding cookie content with GTM to a Next.js website.

    From leveraging the capabilities of Next.js's cookies-next package to exploring the intricacies of GTM integration, we'll bring you on a journey encompassing each essential stage in this process. By the time you finish reading, you'll not only understand the mechanics behind adding cookie consent to your site but also successfully tailor it to fit your user experience, regulatory needs, and data tracking requirements.

    Let's dive into the world of Next.js, cookies, consent banners, and Google Tag Manager. Let's ensure your website not only complies with laws and regulations but also respects user privacy and maintains trust.

    Cookie consent forms part of the broader mandate for data privacy and regulation. Typically, when a user visits a website for the first time, they are presented with a banner or pop-up that explains the website's cookie policies and asks for their consent to use cookies during their browsing session. This approach empowers users to choose whether or not to allow websites to track their online behaviors and preferences.

    Why is cookie consent so critical? The crux of its importance is twofold: user privacy and legal compliance. With cyber threats and privacy breaches becoming common occurrences, it is of utmost importance for users to feel safe while browsing your website. By asking for consent to use cookies, you are showing your users that you respect their data privacy and are transparent about how their data is used.

    On the legal side, several countries and regions have established strict laws and regulations surrounding data privacy. For instance, the European Union's General Data Protection Regulation (GDPR) mandates that websites ask for consent before using cookies to collect and process user data. Similar regulations are in place in other jurisdictions such as California in the United States, where the California Consumer Privacy Act (CCPA) dictates privacy requirements. Non-compliance with these regulations can lead to severe penalties, tarnishing your brand's reputation and leading to significant financial losses.

    In the context of Next.js 13 / 14 applications, adding cookie consent might strike as a complex issue to address. But with the various tools and packages available, such as cookies-next, developers can implement effective cookie consent mechanisms. Moreover, integrating these solutions with Google Tag Manager can further enhance the website's user tracking capabilities while ensuring compliance with data privacy regulations.

    In the upcoming sections, we will delve deeper into the technical steps involved in implementing cookie consent in your Next.js 13 / 14 site, along with integrating it with Google Tag Manager.

    Setting up your Next.js 13 / 14 application

    Before we delve into the specifics of adding a cookie consent banner, the first step is to ensure you have a Next.js 13 / 14 application up and running. If you already have an existing application, you can skip to the next section. However, for those starting from scratch or looking to upgrade to Next.js 13 / 14, this section will guide you through the initial setup.

    Next.js 13 / 14, built by Vercel, is a popular framework for developing React applications. It supports both server-side rendering and static site generation, making it a versatile choice for various types of projects. With features such as image optimization, internationalized routing, and fast refresh, Next.js 13 / 14 enhances both the developer experience and the end user's browsing experience.

    To begin with, ensure you have Node.js and npm (Node Package Manager) installed on your machine as Next.js requires these tools. Node.js is a runtime environment that allows you to run JavaScript on your server, while npm is a package manager for downloading and managing Node.js packages.

    You can install Node.js and npm by visiting the official Node.js download page. Be sure to install the LTS (Long Term Support) version – it is the most stable release and is generally recommended for most users.

    Once you've installed Node.js and npm, you can create your new Next.js application. Open your terminal and navigate to the directory where you want your new project to live. Then, use the following command to create your Next.js app:

    npx create-next-app@latest my-app

    Replace `my-app` with the name of your new application. This command will create a new directory with the specified name, and inside this directory, the `create-next-app` command will generate a new Next.js application.

    Once the installation process is completed, navigate into your new project directory using the command:

    cd my-app

    And then run the following command to start your Next.js application:

    npm run dev

    This command starts your application in development mode. Now, you can open your web browser and navigate to `http://localhost:3000/` to see your new Next.js application in action.

    With your Next.js 13 / 14 application set up, we can now move on to the next step: integrating cookie consent into your application. In the following sections, we will guide you through this process, so you can ensure your website is compliant with data privacy regulations while enhancing user experience.

    Implementing a cookie consent banner in your Next.js application is an essential part of maintaining compliance with data privacy regulations. The primary purpose of this banner is to inform users about the use of cookies on your site and to give them the option to accept or decline. This is an essential component of user privacy and aligns with GDPR rules.

    Several methods exist for implementing a cookie consent banner in your Next.js application. In this guide, we will primarily focus on the cookies-next package, a robust solution for managing cookies in a Next.js application.

    Installation

    Start by installing the cookies-next package in your Next.js application. You can do this by running either of the following commands:

    npm install cookies-next

    or:

    yarn add cookies-next

    These commands will add the cookies-next package to your project dependencies, making its functionality accessible within your application.

    Implementing Cookie Consent Functionality

    After successful installation, you can start implementing the cookie consent functionality. The cookies-next package provides several functions for managing cookies. Two of them, 'hasCookie' and 'setCookie', are particularly useful for managing cookie consent.

    Create a new React component named 'CookieConsent'. In this component, you will make use of the useState and useEffect hooks to manage the state of the cookie consent popup - hence why we have marked is a "use client" component for app router compliance:

    "use client"
    import { setCookie, hasCookie } from 'cookies-next';
    import { useState, useEffect } from 'react';
    
    const CookieConsent = () => {
    const [showConsent, setShowConsent] = useState(false);
    
    useEffect(() => {
    // If no consent cookie is present, show the consent popup
    if (!hasCookie('consent')) {
    setShowConsent(true);
    }
    }, []);
    
    const acceptConsent = () => {
    // When user accepts the consent, hide the popup and set a consent cookie
    setShowConsent(false);
    setCookie('consent', 'true');
    };
    
    if (!showConsent) {
    return null;
    }
    
    return (
    <div>
    <p>We use cookies to improve your experience. By your continued use of this site you accept such use.</p>
    <button onClick={acceptConsent}>Got it!</button>
    </div>
    );
    };

    In this example, the useEffect hook checks if a 'consent' cookie is present when the component mounts, and if not, the consent popup is shown. If the user accepts the consent, the 'showConsent' state changes to 'false', the popup hides, and a 'consent' cookie is set.

    After creating the 'CookieConsent' component, you can add it to your layout or specific pages where you need to show the cookie consent popup.

    Please note that the example above is simplified for explanatory purposes. In a real-world application, you'd also consider styling your banner nicely with CSS or a CSS-in-JS solution like styled-components or emotion. Moreover, you should also provide a way for users to revoke their consent, as required by GDPR.

    Here is a more detailed example with Tailwind CSS:

    "use client"
    import { setCookie, hasCookie } from 'cookies-next';
    import { useState, useEffect } from 'react';
    
    export const CookieConsent = () => {
    const [showConsent, setShowConsent] = useState(false);
    
    useEffect(() => {
    // If no consent cookie is present, show the consent popup
    if (!hasCookie('consent')) {
    setShowConsent(true);
    }
    }, []);
    
    const acceptConsent = () => {
        // When user accepts consent, hide the popup and set a consent cookie
        setShowConsent(false);
        setCookie('consent', 'true');
      
        // Trigger GTM script load
        if (typeof window !== 'undefined') {
          window.dispatchEvent(new Event('updateGTMConsent'));
        }
      };
      const declineConsent = () => {
        // When user declines the consent, simply hide the popup
        setShowConsent(false);
    };
    
      
    
    if (!showConsent) {
    return null;
    }
    
    return (
        <div className='ml-0 fixed bottom-0 left-1/2 w-full sm:w-3/4 md:w-1/2 lg:w-1/3 xl:w-1/3 max-w-2xl min-w-xs py-15 p-8 m-4 bg-blue-500 text-white flex flex-col items-center justify-center transform -translate-x-1/2'> 
        <div> 
            <p>We use some <strong>standard analytics packages</strong> to understand general user behaviour, so we can figure out how to improve our content. This involves some cookies. Are you OK with this?</p> 
        </div> 
        <div className="flex mt-2">
            <button onClick={acceptConsent} className='bg-white text-blue-500 px-4 py-2 rounded mr-2'>Accept</button>
            <button onClick={declineConsent} className='bg-white text-blue-500 px-4 py-2 rounded'>Decline</button>
        </div>
    </div>
    );
    }; 

    In this example, the `hasCookie` and `setCookie` hooks provided by `cookies-next` are used to manage the cookie that stores the user's consent status. If the user hasn't yet accepted the cookies, a banner is shown which contains a message about the use of cookies and an "Accept" button. When the user clicks the button, a cookie named 'consent' is set with the value 'true', indicating that the user has given their consent.

    The design of the banner is styled with Tailwind CSS classes, giving you the flexibility to customize it to fit your branding.

    This type of implementation combines design flexibility with the simplicity of managing cookies directly in your Next.js 13 / 14 application, without relying on more extensive external packages or full-fledged solutions. However, as with other methods, remember that your implementation may need to provide options for users to manage their cookie preferences in more detail, depending on the types of cookies your site uses and the privacy laws you need to comply with.

    The implementation of your cookie consent banner largely depends on the tool you choose to use. As we've discussed in the previous section, the `cookies-next` package is a robust and flexible choice for managing cookies in a Next.js 13 / 14 application.

    However, before proceeding with the integration, it's critical to consider the specific needs of your website and user base. Does your website have a diverse, international audience? Are you required to comply with multiple data privacy laws beyond the GDPR? Do you need to provide granular control over different cookie categories, such as necessary, preference, statistics, and marketing cookies?

    Your answers to these questions can guide your choice of a cookie consent package and how you configure it. For instance, if you need to manage several cookie categories and provide granular consent options, you might consider using a more feature-rich solution like `react-cookie-consent` or `react-cookie-law`. These tools offer extended functionalities such as custom content, onAccept and onDecline callbacks, and the ability to scale up to granular cookie control.

    Once you've chosen an appropriate tool, it's time to configure it to serve your cookie consent banner correctly. Depending on the package you've selected, the configuration steps may vary.

    For the `cookies-next` package used in our example, the configuration is straightforward and mainly involves performing the `setCookie` and `hasCookie` actions at the right times. You can similarly adjust these actions to manage multiple cookies or implement more intricate logic depending on your requirements.

    "use client"
    import { setCookie, hasCookie, deleteCookie } from 'cookies-next';
    import { useState, useEffect } from 'react';
    
    const CookieConsent = () => {
    const [showConsent, setShowConsent] = useState(false);
    
    useEffect(() => {
    if (!hasCookie('consent')) {
    setShowConsent(true);
    }
    }, []);
    
    // example of logic for different cookie types
    const acceptConsent = (cookieType) => {
    setShowConsent(false);
    setCookie(cookieType, 'true');
    };
    
    const declineConsent = (cookieType) => {
    setShowConsent(false);
    deleteCookie(cookieType);
    };
    
    // render your cookie consent banner here
    };

    In this example, we've extended the previous logic to be more flexible. Now, the `acceptConsent` and `declineConsent` functions can handle any type of cookie, allowing granular control over user preferences.

    Remember, implementing cookie consent in the right way is not just about legal compliance - it's also about respecting user preferences and trust. Thus, tailor your cookie consent approach according to your users' needs.

    In the next section, we will explore how to integrate this cookie consent functionality with Google Tag Manager, enabling you to manage user behaviour tracking alongside cookie preferences.

    In the era of data-driven decisions, tracking user interactions with your website is critical. Google Tag Manager (GTM) is a powerful tool that allows you to manage JavaScript and HTML tags used for tracking and analytics on your website without having to modify the code. It simplifies the process of adding and updating tags, including tracking tags for Google Analytics, AdWords, Conversion Tracking, and more.

    Moreover, integrating GTM with your Next.js 13 / 14 application can streamline the connection between your cookie consent functionality and your user tracking setup. This way, you can ensure that specific tracking tags are activated only when users have given their consent, hence maintaining compliance with data privacy regulations.

    Setting up Google Tag Manager within a Next.js 13 / 14 application involves a few steps:

    GTM Account and Container Setup

    Before starting with the code, there are a few administrative tasks to handle. If you don't already have a Google Tag Manager account, create one. After that, you'll need to create a new container for your Next.js application. When creating the container, select the Web platform.

    Once your container is ready, GTM will provide you with a GTM-ID, which is unique to this specific container. Note it down as you'll need it for the code setup.

    Integrating GTM in Next.js 13 / 14

    We have written a full guide for integrating GTM into your Next.js application, with step-by-step walk through and code examples.

    Handling GTM according to user consent

    Now that GTM is integrated into your Next.js 13 / 14 application, you should ensure that it respects user preferences for cookie consent.

    There are two ways to do this:

    Diagram showing two ways to handle cookie consent with GTM.

    You can load GTM and then only allowing tags to fire based on cookie content. This is the preferred approach because it allows Google Analytics 4 to estimate your traffic via the GTM loading, regardless of cookie preferences.

    The other way (some would say the wrong way, but it depends on your preferences!) is to only load GTM based on cookie consent. If cookies aren't consented to, GTM doesn't load at all. Let's look at this approach first:

    Loading GTM based on consent

    In the Cookie Consent section, we discussed how to set a 'consent' cookie when the user accepts the cookie consent. Now, you can check for the presence of this 'consent' cookie in your GTM component and only load the GTM script if the user has given their consent:

    "use client"
    import { hasCookie } from 'cookies-next'
    
    //...
    
    return (
    <>
    {hasCookie('consent') && (
    <Script
    strategy="beforeInteractive"
    src={`https://www.googletagmanager.com/gtm.js?id=${process.env.NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID}`}
    />
    )}
    {/* Other components */}
    </>
    )

    In this code snippet, the GTM script only loads if the 'consent' cookie is present, ensuring GTM is not tracking users who have not given their consent.

    Firing GTM Tags based on Consent (Cookie Content Mode)

    As mentioned, this is the preferred method. It means loading GTM regardless of consent but then only firing tags if cookie consent has been granted. To do this, you need to use the gtag() API and set consent types based on the user choice. Here's an example:

    // create a client component
    "use client"
    // if you are using id=GTM-xxxx. no need for gtmId -- therefore I removed
    import { usePathname, useSearchParams } from "next/navigation"
    import { useEffect } from "react"
    import { getCookie } from 'cookies-next'
    import Script from 'next/script'
    
    export default function Analytics() {
      const pathname = usePathname();
      const searchParams = useSearchParams();
    
      useEffect(() => {
        if (pathname) {
          pageview(pathname);
        }
    
        // Function to load GTM dynamically
        const loadGTM = () => {
          const script = document.createElement('script');
          <Script
          id="gtm-script"
          strategy="afterInteractive"
          dangerouslySetInnerHTML={{
            __html: 
          script.innerHTML = `
            (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
            new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
            j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
            'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
            })(window,document,'script','dataLayer', "GTM-PC67H6PN");
          `,
          }}
          />
          document.body.appendChild(script);
        };
    
        // Load GTM immediately
        loadGTM();
        function gtag(){dataLayer.push(arguments);}
    
        // Check if consent cookie exists
        const consentGiven = getCookie('consent');
    
        // If the consent cookie exists and is set to 'true', grant permissions
        if (consentGiven === 'true') {
          gtag('consent', 'update', {
            'ad_storage': 'granted',
            'analytics_storage': 'granted',
            'functionality_storage': 'granted',
            'personalization_storage': 'granted',
            'security_storage': 'granted'
          });
        } else {
          // Set default settings to 'denied' if no consent cookie is found
          gtag('consent', 'default', {
            'ad_storage': 'denied',
            'analytics_storage': 'denied',
            'functionality_storage': 'denied',
            'personalization_storage': 'denied',
            'security_storage': 'denied'
          });
        }
    
        // Listen for the loadGTM event
        window.addEventListener('updateGTMConsent', () => {
          if (typeof window.dataLayer !== "undefined") {
            gtag('consent', 'update', {
              'ad_storage': 'granted',  // Example consent type
              'analytics_storage': 'granted',  // Another example consent type
              'functionality_storage': 'granted',
              'personalization_storage': 'granted',
              'security_storage': 'granted'
            });
            window.dataLayer.push({
              'event': 'cookie_consent_given',
            });
          }
        });
     
      }, [pathname, searchParams]);
    
      if (process.env.NEXT_PUBLIC_VERCEL_ENV !== "production") {
        return null;
      }
    
      return (
        <noscript>
          <iframe
            src={`https://www.googletagmanager.com/ns.html?id=GTM-PC67H6PN`}
            height="0"
            width="0"
            style={{ display: "none", visibility: "hidden" }}
          />
        </noscript>
      );
    }

    In this example, all cookie types (such as ad_storage) are granted when the user accepts cookies and are denied if the permission is declined.

    We have also set up a cookie_consent_given event in Google Tag Manager for when the user does consent, meaning the tags are fired upon acceptance via the banner. Without this, the behaviour for the first pageview will not be recorded:

    Settings for cookie-consent-given traffic in GTM.

    However, this could be superseded by adding cookie consent as a trigger to your tags:

    Consent Initialisation Trigger in GTM.

    In conclusion, integrating Google Tag Manager with your Next.js application allows for more streamlined management of tracking scripts and enables you to respect user privacy by tying the loading of these scripts with user consent. Now, you not only have a compliant cookie consent system but also a powerful tracking setup controlled by that consent system.

    Implementing Consent Mode in Google Analytics 4 (GA4) with Next.js - instead of simply loading GTM if cookie consent is given - offers a range of benefits:

    Regulation Compliance: One of the immediate advantages of using Consent Mode is its role in ensuring compliance with data protection regulations such as GDPR. By adjusting its behavior based on user consent, Consent Mode allows businesses to continue their data tracking practices within the bounds of the law, enhancing the trust of their users and avoiding potential regulatory penalties.

    Maintaining Data Quality: Amid concerns about user privacy, traditional methods of data collection may lead to significant data gaps if users deny consent for analytics cookies. However, Consent Mode addresses this problem through its conversion modeling and behavioral modeling techniques. These techniques allow GA4 to estimate the unobserved data, thus maintaining the quality of data and preserving the depth of insights that businesses can derive from it.

    User-Centered Design: By centering the user's privacy choices, Consent Mode promotes a positive user experience. It provides users with control over their personal data usage, enhancing their trust in the website. The transparent handling of user data positions the website as respectful of user privacy, which can boost reputation and user engagement.

    Versatility in Implementation: The implementation of Consent Mode with GA4 in a Next.js project is both flexible and adaptable. It can cater to the basic requirement such as 'accept' or 'deny' options and also be easily extended to more comprehensive and granular consent choices. This versatility allows businesses to tailor the implementation to best suit their needs and the specific requirements of their user base.

    Integration with Consent Management Platforms (CMPs): Consent Mode is designed to work directly with CMPs. This integration optimizes the handling of user consent, making it easier for businesses to manage and document user consent for cookies. It offers an efficient, automated way to remain compliant with GDPR and other data protection regulations.

    Relevance in the Cookieless Future: Amid the impending phase-out of third-party cookies, Consent Mode is a forward-looking feature that holds relevance in the era of privacy-focused web browsing. With anonymous ping tracking and behavioral modeling, Consent Mode paves the way for a viable avenue of data analysis in an increasingly cookieless future.

    Incorporating Consent Mode in GA4 within a Next.js environment, therefore, presents an effective solution for businesses to balance the need for insightful data analysis with the responsibility to respect user privacy.

    While there are significant benefits of using GA4's Consent Mode with Next.js, it's also essential to acknowledge its potential limitations. These challenges are an integral part of the landscape when handling user consent and data privacy, helping to inform a more balanced view and encouraging an informed approach to using Consent Mode.

    Limited Data Collection: One of the foundational principles of Consent Mode is its respect for user choices about consent. While this is a plus on the privacy front, it also means that if a user declines consent, certain data will understandably not be available. The anonymized pings that GA4 sends to Google in these situations can't fill these gaps completely, leading to potentially significant data limitations.

    Behavioral Modeling Constraints: To offset the challenge of limited data, GA4 uses behavioral modeling techniques (if you implement Consent Mode correctly). However, these estimations are based on patterns observed among consenting users and may not accurately represent the behavior of users who deny consent. Furthermore, behavioral modeling requires enough data to train the model accurately. In situations where there isn't sufficient data, the quality and reliability of the modeled insights may be compromised.

    Advanced Understanding Required: GA4's Consent Mode operates based on users' advanced and basic consent states, making it crucial to understand these states thoroughly and implement them correctly. Any gaps in this understanding can lead to inaccurate data interpretation, potentially leading to misinformed business decisions. Although GA4 offers educational resources to help understand these elements, the learning curve can be steep.

    Changes in User Behavior: Utilizing behavioral modeling to supplement gaps in data means relying on machine learning algorithms. While these algorithms can make effective estimations, they are still subject to potential errors and changes in user behavior that can disrupt predicted patterns.

    Adjusting to GA4: For those migrating from Universal Analytics (UA) to GA4, there might be some initial challenges in adjusting to GA4's different data model, which focuses on events instead of sessions and pageviews. This difference could mean reconfiguring metrics and standard reports, and learning new ways to set up conversions and custom tracking.

    Privacy Concerns: GA4 has been designed with privacy regulations in mind, and Consent Mode is a testament to that. However, as with any technology dealing with user data, there could be potential privacy concerns that need to be regularly audited and addressed to ensure compliance with regulations like GDPR. This is why I also gave you the alternative option, which is to not load GTM at all when consent is denied.

    Having a clear understanding of these limitations helps in making a well-rounded decision about implementing GA4's Consent Mode within Next.js. It encourages an optimized and thoughtful use of this feature, underlining the importance of a data-driven yet privacy-respecting approach.

    Summary

    Implementing cookie consent on your website is crucial for legal compliance and for securing user trust. But as this comprehensive guide has demonstrated, it certainly isn't an insurmountable challenge. Whether you choose to adopt the tools and features that Next.js 13 / 14 provides, utilize external packages like `cookies-next`, `react-cookie-consent` or `react-cookie-law`, the path to a more secure, privacy-compliant, and user-friendly website is clear.

    Remember, the key to a successful cookie consent implementation lies in ensuring transparency, giving users control, documenting your site's cookie usage, and adhering to privacy laws. If these principles guide your approach, your website will be better equipped to offer a respectful, compliant, and engaging user experience.

    Richard Lawrence

    About Richard Lawrence

    Constantly looking to evolve and learn, I have have studied in areas as diverse as Philosophy, International Marketing and Data Science. I've been within the tech space, including SEO and development, since 2008.
    Copyright © 2024 evolvingDev. All rights reserved.