When utilizing Google Analytics in conjunction with Next.Js, encountering the error message "window.gtag is not

Encountering an error on page load with the message:

window.gtag is not a function

Using Next.js version 14.0.4.

All existing solutions seem to hinder data collection, preventing the website from setting cookie consent correctly. I am uncertain about the next steps to take. The code shows that multiple data types need to be collected. Initially, it worked with just one type but after upgrading to Next.js 14, errors started to occur. The packages used were @types/gtag.js and client-only.

The layout.tsx file appears as follows:

        <html lang='en'>
            <head>
                <GoogleAnalytics />
            </head>
            <body className='overflow-x-hidden mx-auto w-[100vw] flex flex-col items-center justify-center box-border'>
                <Toaster />
                <Navbar />
                <div className="right-10 md:right-0 -top-10 md:top-10 absolute w-[100vw] h-[230px] gradient-02 -z-50 hidden sm:block" />
                <main className='w-[100vw] overflow-x-hidden mx-auto'>
                    {children}
                </main>
                <Footer />
                <CookieBanner />
            </body>
        </html>

This is the CookieBanner Component:

'use client';
/**
 * Type Definition
 */
type ConsentState = {
    adStorage: boolean
    analyticsStorage: boolean
}

export default function CookieBanner() {

    // Define useState
    const [cookieConsent, setCookieConsent] = useState<ConsentState>();

    // useEffect checks if cookie_consent is already set
    useEffect(() => {
        const storedCookieConsent = getLocalStorage("cookie_consent", null);
        setCookieConsent(storedCookieConsent);
    }, [setCookieConsent]);

    // useEffect updates once cookieConsent is updated to update the consent values
    useEffect(() => {
        const adStorage = cookieConsent?.adStorage ? 'granted' : 'denied'
        const analyticsStorage = cookieConsent?.analyticsStorage ? 'granted' : 'denied'

        // Check if window.gtag is there
        if (typeof window !== 'undefined' && typeof window.gtag !== 'undefined') {
            window.gtag("consent", 'update', {
                'analytics_storage': analyticsStorage,
                'ad_storage': adStorage,
            });
            console.log("Cookies set")
        } else {
            console.warn("gtag is not defined, cannot update consent.");
        }

        setLocalStorage("cookie_consent", cookieConsent);

    }, [cookieConsent]);


    return (
        <div
            className={`my-10 mx-auto max-w-[90%] md:max-w-screen-sm
                        fixed bottom-1 left-0 right-0 
                        ${cookieConsent != null ? "hidden" : "flex"} px-3 md:px-4 py-3 justify-between items-center flex-col gap-4 text-[17px]  
                        bg-[#2E3A59] rounded-lg shadow-white shadow z-50`}
        >
            <div className='flex flex-col sm:flex-row items-center justify-between gap-y-3 w-full'>
                <div className='text-center sm:text-left text-stone-200 w-fit min-w-[50%]'>
                    <Link href="/datapolicy" aria-label='Link to Data Policy'><p>This site uses <span className='font-bold text-primary hover:underline duration-1000'>Cookies.</span></p></Link>
                </div>
                <div className='flex gap-4 items-center justify-center w-full'>
                    <Button onClick={() => setCookieConsent({ adStorage: false, analyticsStorage: true })} size="md" variant="ghost" className='max-w-[33%] flex flex-col items-center justify-center' aria-label='Accept required Cookies'>
                        <span>Accept</span>
                        <span className='text-[11px]'>(required only)</span>
                    </Button>
                    <Button onClick={() => setCookieConsent({ adStorage: true, analyticsStorage: true })} size="md" variant="secondary" className='max-w-[60%]' aria-label='Accept all Cookies'>
                        Accept All
                    </Button>
                </div>
            </div>
        </div>
    )
};

I have tried various approaches like moving GoogleAnalytics to different locations in the layout and making changes to the cookie banner with no success. Despite conditionally checking for window.gtag, the error persists, hindering data collection.

Thank you in advance!

Answer №1

It seems like the issue is that typescript doesn't recognize gtag in your window object, possibly because it was added in a script within _app or another component.

If gtag is only used in the CookieBanner component, you can include the following code snippet at the top:

declare global {
  interface Window {
    gtag: any;
  }
}

Otherwise, you will need to add it to a higher level component for typescript to be aware of it.

Answer №2

To overcome this issue, you can establish the 'gtag' function as a global declaration in your Next.js project. One effective method is to generate declaration files for global variables using the following steps:

  1. Create a new Typescript declaration file within your project's root directory.

custom.d.ts

interface Window {
   gtag: (...args: any[]) => void;
}
  1. Modify tsconfig.json to include the pathway to the folder containing your declaration files, ensuring that the typescript compiler can acknowledge them.

    {
       "compiler options": {
          "include": ["next-env.d.ts", "custom.d.ts"]
       }
    }
    
  2. Implementation in your code:

    // insert your code here
    
    declare global {
       interface Window {
           gtag: (...args: any[]) => void;
       }
    }
    
    // insert your code here
    

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilize middleware nesting in Express.js

I am interested in implementing middleware to validate requests based on a RAML file. Below is an outline of my current code: // dependencies let resources = require('osprey-resources'); let errorHandler = require('request-error-handler&apo ...

Exploring the Power of React's Ref API

Currently, I am following tutorials on Udemy by Max where he discusses how to work with Ref Api's in React 16.3. In one of the lectures, he demonstrated creating a ref inside a container class, not App.js, using this.lastref = React.createRef();. He ...

Creating a map in Typescript initialized with a JSON object

In my Typescript class, there is a map that I'm trying to initialize: public map:Map<string,string>; constructor() { let jsonString = { "peureo" : "dsdlsdksd" }; this.map = jsonString; } The issue I'm encounte ...

While attempting to utilize npm install, I encounter an error on a discord bot stating "msvsVersion is not defined."

Struggling with self-hosting a TypeScript discord bot, the setup process has been a puzzle. It's supposed to generate a build directory with an index.js file, but it's unclear. Installed Visual Studio Build Tools 2017 as required, yet running npm ...

Tips for creating a personalized asynchronous Express handler that seamlessly receives specific typed parameters

In my quest to create a unique Express endpoint wrapper, I aim to wrap async functions and handle errors effectively. The current implementation is basic but functional: import type {Request, RequestHandler, Response} from 'express'; type Handle ...

Tips for utilizing a printer to print HTML content in PHP

I've stored HTML content in a variable as shown below: $message ="<div> <table align='center'> <tr><td><h1>Tipo de Documentos Report</h1></td></tr> <tr><td>< ...

A custom JavaScript function designed to replicate Excel's functionality of dividing numbers by thousands

I've noticed a unique behavior in Excel where when a cell is in focus and you enter, for example, 1500.32, it displays as 1 500.32. However, once you click enter or move away from the cell, it changes to 1 500.32. I'm intrigued by how this works. ...

Foreign keys in a one-to-many relationship with Sequelize.js

I am in the process of developing a survey application using Node.js/Express and MySQL incorporating Sequelize.js ORM. However, I am encountering difficulties while establishing the relationship between the two models. My goal is to have the Questions&apo ...

Extensible generic type/interface in Typescript

Looking to create a versatile base interface or type that can adapt its properties based on the generic object it receives. It might look something like this: interface BaseObject<Extension extends object = {}>{ a: string; b: string; {...Ext ...

Is it possible to use a full-width material-ui Button inside a Badge component?

Within a grid, I had initially used fullWidth on a Button to make it expand and fill the container. Everything was functioning correctly until I enclosed the Button in a Badge element. Now, the fullWidth property is not being applied, and the button rever ...

Generic Typescript Placeholder Design

Imagine you have the following data: const dataA = { name: "John", age: 25, attributes: {specificA: "hello", specificA2: 14, nonspecific:"Well"}, } const dataB = { name: "Lisa", age: 38, attributes: {spe ...

What is the best way to play AudioBuffer on an iPhone device?

When retrieving audio data from a stream, I encounter an issue with playing audio on iPhone Safari. The sound does not play unless I allow mediaDevice access for audio. Is there a way to play the audio without having to grant this permission? This is the ...

Converting HTML into an object using $compile

I am currently working on calling an HTML template and want to bind the data with Angular. I successfully retrieve the data to bind and the HTML, but when I try to compile it, it returns all the HTML binded as an object. How can I convert it back to plain ...

How can I replay an HTML audio element?

I created an HTML5 page with an audio element for playing music in mp3 format. However, when the music plays to the end, it stops and I'm using JavaScript to control the audio element. Even so, I can't seem to replay it, only stop it. Is there a ...

Having trouble saving text in a textbox?

Is there a way to store text in a text box for future use? Every time I reload the page, the text in the box disappears. I have attempted the following solution: <td align='left' bgcolor='#cfcfcf'><input type="text" name="tx ...

Unable to extract all advertisements from Facebook Marketplace

https://i.stack.imgur.com/xEhsS.jpg I'm currently attempting to scrape listings from Facebook marketplace, however, only the first listing is being scraped. Does anyone have any suggestions on how I can scrape the entire list of listings? CODE (async ...

None of the Views are rendered after executing RedirectToAction

I created a Login Page that redirects to the required page after validation. However, when it redirects, the previous Login page view appears instead of the expected view. Below is the JavaScript code I am using: function abc() { var email = ...

Combining multiple SCSS classes from various files in Next.js

Despite an extensive search for information on this topic yielding no success, I would like to inquire... I have developed a component that generates "h1-h6" tags with customized styles: import {HTagProps} from './HTag.props' import styles from ...

Exploring the Power of NextJS, Apollo, and WPGraphQL for Efficiently Handling Large Datasets

Attempting to retrieve more than 100 records for a WPGraphQL query using Apollo during getStaticProps. Jason from WPGraphQL suggested utilizing pagination and combining the results into a new Array (or Object?). The problem arises when trying to combine t ...

The PHP function is failing to communicate with jQuery and Ajax

Having trouble with PHP return to jQuery/Ajax functionality, When I try to edit an item, the error message displays even though the success function is executed. On the other hand, when attempting to delete an item, nothing is displayed despite the succes ...