DEV Community

Cover image for Google Analytics With React Typescript
Itay Eylath
Itay Eylath

Posted on • Edited on

Google Analytics With React Typescript

What is GA ⁉️ 😯

Google Analytics (GA) is a powerful tool for tracking and analyzing website traffic and user behavior.

Here's how it operates:

GA collects data from your website using a JavaScript tracking code that you embed in your site’s pages. This code gathers information about your visitors and sends it to Google's servers, where it is processed and made available in various reports.

How's the implementation Work?

useGoogleAnalytics.ts as custom hook is the primary file for integrating with GA.
We're using an object to handle GA integration for flexibility and future-proofing.

GoogleAnalyticsProvider.tsx as a provider component to wrap the Routes

App.tsx modify your file by add GoogleAnalyticsProvider component

Why This Approach? ♻️

  • Support multiple: functionalities, easily manage page views, event tracking, and post-initialization configurations.

  • Future proofing: If we switch from ReactGA to another solution (e.g., a future GA version), we only need to update this object, not every instance in our codebase.

  • React Way: This method aligns with React’s best practices by using a component to handle side effects.

Comments In the code

Search it on the App // Recommend: and // Remark: to understand logic and getting the implantation better in your App!

GitHub Repo: 'google-analytics-react-ts'

useGoogleAnalytics.ts.tsx

import { useEffect } from 'react';
import ReactGA from 'react-ga4';

// Recommend: implement it with env variables to keep it as a secret 
export const trackingId = "GA_ID"
const appVersion = "APP_VERSION"
// Remark: use user ID in your app to make the analyze better
// Recommend: implement it with Redux 
const id = "user-id"

const useGoogleAnalytics = () => {

  useEffect(() => {
    if (trackingId) {
      try {
        ReactGA.initialize([
          {
            trackingId,
            gaOptions: {
              anonymizeIp: true,
              clientId: id
            }
          }
        ]);
        ReactGA.set({ app_version: appVersion });
      } catch (error) {
        // Recommend: reporting this error to an error tracking service
        console.log("Error initializing Google Analytics", { Error: error });
      }
    }
  }, [id]);

  const setOption = (key: string, value: unknown) => {
    ReactGA.set({ [key]: value });
  };

  const setUserId = (userId: string | number) => {
    setOption("userId", userId);
  };

  const sendData = (type: string, data: Object) => {
    ReactGA.send({ hitType: type, ...data });
  };

  const trackPageView = (pagePath?: string) => {
    if (!pagePath) {
      pagePath = location.href;
    }

    setOption('app_version', appVersion);
    sendData("pageview", { page: pagePath });
  };

  const trackEvent = (category: string, action: string, label?: string, value?: number) => {
    setOption('app_version', appVersion);
    ReactGA.event({ category, action, label, value });
  };

  return {
    setOption,
    setUserId,
    trackPageView,
    trackEvent,
  };
};

export default useGoogleAnalytics;
Enter fullscreen mode Exit fullscreen mode

GoogleAnalyticsProvider.tsx

import React, { useEffect, PropsWithChildren } from 'react';
import { useLocation } from 'react-router-dom';
import useGoogleAnalytics, { trackingId } from '../hooks/useGoogleAnalytics';

const GoogleAnalyticsProvider: React.FC<PropsWithChildren<{}>> = ({ children }) => {
    const { trackPageView } = useGoogleAnalytics();
    const location = useLocation();

    useEffect(() => {
        if (trackingId) {
            try {
                trackPageView(location.pathname + location.search);
            } catch (error) {
                // Recommend: reporting this error to an error tracking service
                console.log("Error executing trackPageView Google Analytics", { Error: error });
            }
        }
    }, [location, trackPageView]);
    // Remark: this allows GoogleAnalyticsProvider to wrap other components without affecting the UI
    return <>{children}</>;
}

export default GoogleAnalyticsProvider;
Enter fullscreen mode Exit fullscreen mode

App.tsx

import { BrowserRouter as Router, Route, Routes, useLocation } from 'react-router-dom';
import './App.css';
import GoogleAnalyticsProvider from './providers/GoogleAnalyticsProvider';

const App: React.FC = () => {

  return (
    // Remark: check in your App, the optimal placement for GoogleAnalyticsProvider-
    // to ensures early initialization to track all route changes but the errors still be captured (if you use an error tracking service)
    <GoogleAnalyticsProvider>
      <Routes>
        <Route
          path="/"
          element={
            <div>
              Google Analytics React TypeScript
            </div>
          }
        />
      </Routes>
    </GoogleAnalyticsProvider >
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

2 Ways Of Implementation

1 . General Tracking (the current repo) 😎

💪 Pros:

  • Simple Initialization: Your setup ensures that Google Analytics is initialized only once when the GAInitializer component mounts.
  • Page View Tracking: By providing a method to track page views, you can manually ensure that each page visit is logged.
  • Custom Events: The trackEventBuilder method offers flexibility to track various user interactions.

👎 Cons:

  • Manual Page View Tracking: Since you're only wrapping the App component and not each route change, you must manually call trackPageView on each route change, which is prone to human error if forgotten.
  • Lack of Route Change Tracking: Without automatic tracking of route changes, you might miss logging some page views if trackPageView is not called explicitly in each component.
  • Limited User Context: Your implementation does not track detailed user interactions or demographic data beyond what GA collects by default.

2. Add Event Tracking Function To EVERY Event We Want To Track 😴

💪 Pros:

  • Granular Control: you have precise control over exactly which events are tracked and how they are labeled, allowing for very detailed and specific analytics data.
  • Customization: each event can be customized with specific category, action, label, and value, providing rich insights into user behavior and interactions.

👎 Cons:

  • Maintenance Overhead: as your application grows, managing numerous individual tracking calls can become time-consuming and difficult to maintain consistently.
  • Code Duplication: you might end up repeating similar tracking code across multiple components or functions, leading to less DRY (Don't Repeat Yourself) code.

Content Security Policy (CSP) 🚔

To implement GA, include the GA script in your web application and set up tracking in your code. Configure your CSP to allow connections to GA servers by adding 'https://www.google-analytics.com' to your script-src and connect-src directives. If using Google Tag Manager, also include 'https://www.googletagmanager.com' in your script-src directive, and always test your CSP configuration thoroughly.

References ©️

Medium: 'Implementing Google Analytics to React with TypeScript'

YouTube: 'Add Google Analytics in React JS'

Top comments (1)

Collapse
 
nivsorek profile image
Niv Sorek

Thanks, man!
Very informative and clear.
I particularly liked the way you implemented it with the custom hook!

Looking forward to your upcoming content.