What is the proper way to enable the Google Analytics cookie only once another consent cookie has been set and is marked as "true"?

I am currently using React/Nextjs on my website along with the react-cookie-consent library. This setup generates a pop-up where users can agree to the cookie policy, and if they do, a CookieConsent with the value "true" is set.

In addition to this, I wish to integrate Google Analytics to track user activity only after they have provided consent by clicking "Accept" on the pop-up.

However, there seems to be an issue as the Google Analytic cookies _ga and G_ENABLED_IDPS are being set even before the user interacts with the pop-up. Interestingly, this behavior was observed specifically on the Microsoft Edge Browser, while Chrome did not exhibit this problem.

The snippet below represents the code in my current _document.js file:

<Head>
          {/* Global Site Tag (gtag.js) - Google Analytics */}
          <script
            async
            src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
          />
          <script
            dangerouslySetInnerHTML={{
              __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}            
            gtag('js', new Date());
            
          `}}
          />
          
          <script type="text/javascript" src="/static/blockReactDevTools.js" />
          
          <link href="https://fonts.googleapis.com/css?family=Cabin&display=swap" rel="stylesheet" />

</Head>

After experimenting with various suggestions from online resources, I revised the code but still encountered the same issue:

<Head>
          {/* Global Site Tag (gtag.js) - Google Analytics */}
          <script 
            async
            src=`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`
          />

          <script        
            dangerouslySetInnerHTML={{
                __html: `
              
              var gtagId = ${GA_TRACKING_ID}';

              window['ga-disable-' + gtagId] = true;

              window.dataLayer = window.dataLayer || [];

              function gtag() { dataLayer.push(arguments); }   

              gtag('js', new Date());

              // Rest of the script continues here...
           `}}
          />

          <script type="text/javascript" src="/static/blockReactDevTools.js" />

          <link href="https://fonts.googleapis.com/css?family=Cabin&display=swap" rel="stylesheet" />

</Head>

I am unsure whether this issue pertains to Nextjs or is simply due to an oversight on my part.

Could someone provide me with a working solution?

EDIT: I attempted to implement the "Universal Analytics" approach suggested by others, which caused my helper functions for logging events and pageviews to fail (as shown in the image below). Should I also include the gtag manager?

https://i.stack.imgur.com/wRp2D.png

Answer №1

To implement gtag's consent configuration options, refer to the documentation provided by Google here. Before executing any data measurement commands in the header (config or event), you can specify the following settings:

gtag('consent', 'default', {
  'ad_storage': 'denied',
  'analytics_storage': 'denied'
});

After obtaining user approval or if a consent cookie is present, update the settings using:

gtag('consent', 'update', {
  'ad_storage': 'granted',
  'analytics_storage': 'granted'
});

For those employing Facebook Pixel, a similar approach can be taken. For example: fbq('consent', 'revoke'); followed by fbq('consent', 'grant').

Answer №2

If you're setting up Google Analytics on your website, it's important to follow GDPR guidelines. Instead of opting-out where the GA cookie is set automatically upon client request, consider implementing an opt-in approach. This means the GA cookie will only be set once the user consents to it.

To achieve this, you can async load the gtag.js file after the user has given consent. It's recommended to use a library like CookieConsent which generates a popup for users to provide their consent and sets a consent-cookie accordingly.

For implementing this on your website, refer to the following links:

Reference:

Generate the layout code for your cookie banner by clicking Start Coding here: https://github.com/osano/cookieconsent/blob/dev/examples/example-7-javascript-api.html

The code snippet below needs to be included in the <head> section of every page:

<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.1/cookieconsent.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.1/cookieconsent.min.js" data-cfasync="false"></script>
// Remaining script implementation provided in original text

Note:
Ensure that gtag.js is loaded on each page-load post user consent. Use event_callback to track gtag events. It's important to declare the function gtag() globally before use to prevent errors.

// cookie-check not required if gtag is globally scoped
//if(popup.hasConsented()) { 
    // Sample event tracking using gtag function
//}

No additional pageview event needs to sent unless specific path details are required. The setCookies() function already includes the current document path along with the configuration.

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

Error: Requested URLs must be absolute and valid

Encountering an error while using the latest version of openid-client in a next-js application. Unhandled Runtime Error TypeError: only valid absolute URLs can be requested 3 | 4 | const oidcConfig = async () => { > 5 | const issuer = await I ...

Is it possible to customize the close icons on the autocomplete feature in Material UI?

Is there a solution to change the icon while keeping its function when clicked? I am looking to replace this Icon <Autocomplete multiple id="checkboxes-tags-demo" options={top100Films} disableCloseOnSelect getOpt ...

Error: The callback specified is not a valid function

Here is a function example: reportAdminActions.reportMemberList(project, function(data) { console.log(data); }); This particular function gets called by another ajax operation, similar to the one shown below: reportMemberList: function(projectId, ca ...

Having trouble with a jquery link not working even after removing the 'disabled' class

I am currently using a script that disables links with the class "disabled" //disable links $(document).ready(function() { $(".disabled a").click(function() { return false; }); }); In addition, I have another script that toggles the disabled s ...

Establishing the Initial State of React Context with Data from Apollo Query

In my React.js project, specifically with Next.js, I have implemented a global React context for managing user data. Here is how the context is structured: const UserContext = createContext<{ user: User | undefined; setUser: (u: User | undefined) =& ...

React-useForm: formData is not transmitted during the initial request when using react-query

Having attempted to integrate react-query and react-useform, I encountered an issue where the form data managed by use-form appears empty when attempting to send it through an API request. It seems there is a flaw in my code as the data is successfully sen ...

It seems like the Next.js middleware.ts file is running twice, which may also be causing the app to render twice

After investigating the middleware, we discovered that it is executing functions twice, including the application itself. To troubleshoot this issue, I removed the "favicon" from a matcher in the resources where middleware is used because there were talks ...

Removing one element from an array with mongoose

As a newcomer to web development, I am currently working on creating a todo app. Below is the schema and model that I have: const tdSchema = new mongoose.Schema({ category: { type: String, required: true, unique: true }, tds: { type: ...

How can I adjust the width of a handle/thumb for NoUiSlider?

Looking to adjust the width of a NoUiSlider using CSS: .noUi-horizontal .noUi-handle { width:8px; height:25px; left: 0px; top: -8px; border: 0px solid #000000; border-radius: 0px; background: #000; cursor: default; box- ...

Using ajax for sending data is a breeze, but I am encountering trouble when attempting to receive data back from

I have a function that retrieves a value from an input and sends data through ajax to another PHP file. However, I am facing an issue where I cannot retrieve the result back from the PHP file even though I echo it in the ajax success function. <script&g ...

Trying out the Material-UI Textfield select component using jest testing framework

Currently, I am in the process of writing a test to validate a basic Material-UI Textfield select component. The objective of this test is to demonstrate that selecting an option triggers the corresponding event. Below is the representation of the compone ...

Obtain the value for every span class and insert the corresponding string into the input field

I have a series of tags labeled as 'tag-label' in spans. My goal is to combine the values of these tags and insert them into an input field, separating each value with commas. To demonstrate this, I've created a JSFiddle that showcases the ...

Before the UseEffect updates the state, the page is already rendered

I am looking to prepend a textbox that makes an API call when the page loads initially. Even though both console logs are able to print out the result, it appears as NULL in the render. My assumption is that it renders before the API call. How can I make ...

Placing a moveable object in a designated spot for dropping at the desired location

I've been attempting to clone and drop a draggable object at the exact position within a droppable area where the drop event takes place. While I have come across examples online that demonstrate appending draggables to droppables, they all tend to re ...

Leverage JavaScript E4X to cleverly rename specific XML elements

Currently, I am using JavaScript to manipulate XML without involving the DOM in a browser context. I need assistance with creating an E4X expression that can rename a list of tags based on a given substring. The challenge is that I may not know the exact t ...

Is there a way to have dynamic content from angularJS show up in an iframe?

When working with tabular content generated using ng-repeat in AngularJS, it is important to find a way to display this content within an iframe. The goal is to allow users to drag and resize the content as needed. However, using the iframe src attribute ...

What is the best way to implement validation for a textfield to prevent submission if a negative value is entered?

I am working with a text field of type number and I have successfully set a minimum value of 0 to ensure that negative values are not accepted. However, I have encountered an issue where I am unable to delete the 0 once it is entered. Is there a way to fix ...

Angular template not refreshing automatically

Within my controller: $scope.deleteUser = function(user){ $.ajax({ url: "/users/" + user.id.toString(), method: "DELETE", success: function(result){ $scope.users = result["users"]; ...

Adjusting a parameter according to the width of the browser window?

Using Masonry, I have implemented code that adjusts the columnWidth to 320 when the screen or browser window width is less than 1035px. When the width exceeds 1035px, the columnWidth should be 240. However, the current code keeps the columnWidth at 320 re ...

Leverage videojs-vr within a Vue.js component

I have been experimenting with integrating the videojs-vr package, which I installed through npm, into a Vue.js component. However, I encountered an error: TypeError: videojs is not a function at VueComponent.mounted (VR.vue?d2da:23) at callHook (vue.esm. ...