DEV Community

Tuhar Prajapati
Tuhar Prajapati

Posted on • Edited on

1

The Best Way to Integrate Google Analytics in React: Event Emitter

Google Analytics (GA) is a powerful tool for tracking user activity in applications, but integrating it directly into React apps can get messy. Tracking code can end up scattered across your components, making the app harder to maintain. A cleaner way to handle this is by using an Event Emitter pattern, which helps you centralize your tracking logic and keeps your code modular and easier to manage as your app grows.

In this blog, we’ll explore the general (direct) approach and the Event Emitter approach in the context of a React application.

General Approach in React:

Here’s how you might implement Google Analytics directly after initializing it:

import { useEffect } from "react";

const Register = () => {

  useEffect(() => {
    window.gtag('event', 'page_view', {
      page_path: '/register',
    });
  }, []);

  const handleClick = () => {
    window.gtag("event", "click", {
      event_category: "Button",
      event_label: "Signup Button",
      value: 1,
    });
  };

  return (
    <button onClick={handleClick}>Sign Up</button>
  );
};

export default Register;

Enter fullscreen mode Exit fullscreen mode

While this works for simple applications, it becomes problematic in larger projects due to:

  • Code Duplication: Similar tracking logic is repeated in multiple components.

  • Tight Coupling: The tracking logic is embedded in the component, making it hard to maintain or replace Google Analytics.

  • Scalability Issues: Tracking events across multiple components can lead to inconsistencies.

Event Emitter Approach in React

With the Event Emitter approach, you decouple the tracking logic from React components. Instead of calling gtag directly, components emit events, and a centralized analytics service listens for and handles these events.

Create AnalyticsManager Class

import { EventEmitter } from "events";

class AnalyticsManager {
  constructor() {
    this.analyticsEmitter = new EventEmitter();

    this.analyticsEmitter.on("trackEvent", (eventData) => {
      const { category, action, label, value } = eventData;
      this.trackEvent(category, action, label, value);
    });

    this.analyticsEmitter.on("trackPageView", (path) => {
      this.trackPageView(path);
    });
  }

  initAnalytics = (measurementId) => {
    const script = document.createElement("script");
    script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`;
    script.async = true;
    document.head.appendChild(script);

    window.dataLayer = window.dataLayer || [];
    window.gtag = function () {
      window.dataLayer.push(arguments);
    };

    window.gtag("js", new Date());
    window.gtag("config", measurementId);

    this.measurementId = measurementId;
  }

  trackEvent = (category, action, label, value) => { 
    if (!this.measurementId) {
      console.error("Google Analytics is not initialized.");
      return;
    }

    if (window.gtag) {
      window.gtag("event", action, {
        event_category: category,
        event_label: label,
        value: value,
      });
    } else {
      console.error("Google Analytics gtag function not found.");
    }
  }

  trackPageView = (path) => {
    if (!this.measurementId) {
      console.error("Google Analytics is not initialized.");
      return;
    }

    if (window.gtag) {
      window.gtag("event", "page_view", {
        page_path: path,
      });
    } else {
      console.error("Google Analytics gtag function not found.");
    }
  }

  emitEvent = (eventName, eventData) => {
    this.analyticsEmitter.emit(eventName, eventData);
  }
}

export default new AnalyticsManager();


Enter fullscreen mode Exit fullscreen mode

Place the initialization logic in a standalone module or utility file. This ensures it's executed only once during the application's lifecycle.

// analyticsSetup.js
import AnalyticsManager from "./AnalyticsManager";

AnalyticsManager.initAnalytics("YOUR_MEASUREMENT_ID");


Enter fullscreen mode Exit fullscreen mode

Import this setup file in your entry point (e.g., index.js):

// index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import "./analyticsSetup"; // Ensures analytics initialization runs once

ReactDOM.render(<App />, document.getElementById("root"));

Enter fullscreen mode Exit fullscreen mode

Use in components

import { useEffect } from "react";
import AnalyticsManager from "./AnalyticsManager";

const Register = () => {

 useEffect(() => {
     AnalyticsManager.emitEvent("trackPageView", "/register");
  }, []);

  const handleButtonClick = () => {
    AnalyticsManager.emitEvent("trackEvent", {
      category: "User Interaction",
      action: "Click",
      label: "Signup Button",
      value: 1,
    });
  };

  return <button onClick={handleButtonClick}>Sign Up</button>;
};

export default Register;

Enter fullscreen mode Exit fullscreen mode

Why Use Event Emitters for Analytics?

  1. Centralization: All tracking logic is handled in one place, reducing duplication and errors.

  2. Flexibility: You can easily integrate multiple analytics tools without modifying individual components.

  3. Scalability: Adding new tracking events or modifying existing ones becomes straightforward.

Using an Event Emitter to integrate Google Analytics in React applications is a game-changer for maintainability and scalability. By separating concerns, you can keep your components clean and focus on their primary role: rendering the UI.
This is my first guide and more to come. If you found this guide helpful, feel free to leave a comment or share it with your network. Happy coding! 🚀

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (2)

Collapse
 
amslezak profile image
Andy Slezak

This came just at the right time! Thank you!

Collapse
 
tusharprajapatiii profile image
Tuhar Prajapati • Edited

Happy to hear! this was helpful for you

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →