DEV Community 👩‍💻👨‍💻

Cover image for Setting Up Google Analytics with React Context in Next.js
Mark Khuzam
Mark Khuzam

Posted on

Setting Up Google Analytics with React Context in Next.js

Google Analytics is a free tool offered by Google to assist you in analyzing your web traffic. The topic of Analytics and User Tracking is a pretty deep dive and out of the scope of this tutorial, if you'd like to learn more about Analytics, this article offers a good starting point for what Google Analytics is and why it's important to your business.

If you're completely new to GA, you'll want to visit the application and set up an account. (GMail required)

After you're done signing up, you'll be provided with a Tracking ID for your website.

It should look something like

UA-XXXXXXXX-X

React + Google Analytics

There are many great tutorials about setting up GA in a React application, and most will get you to where you need. Where this tutorial differs is the use of React Context.

By utilizing a Context, we can keep a React State object that holds information to handle multiple use cases, such as, a unique User ID, multiple GA Tracking ID's, user provided Tracking ID's, and more.

To handle communicating with GA we'll use a popular utility library, react-ga

Installing the library can be done by using the command

    yarn add react-ga
Enter fullscreen mode Exit fullscreen mode

Creating the Context

We'll start by creating a Tracking Context which will be used to provide our App with the an API for logging events as well as initializing GA when the app loads.

    // contexts/trackers.jsx
    import React from  'react';

    const TrackingID = 'UA-XXXXXXXX-X';

    const TrackingContext = React.createContext();

    function TrackingProvider(props) {
        return <TrackingContext.Provider {...props} />
    }

    const useTracking = () => React.useContext(TrackingContext);

    export { TrackingProvider, useTracking };
Enter fullscreen mode Exit fullscreen mode

Up to this point, what we've done is create a Context that we can use to wrap Components that will utilize tracking. Most applications will benefit from wrapping the entire application with a Tracking Context but use cases will vary so I recommend applying it where you think is best for your application.

Integrating into Next.js

Next.js is an wonderful React framework that offers a quick way to implement SSR (server side rendering). If you've never used Next before, I recommend following their Create a Next.js App tutorial.

To provide tracking to all of our components, we'll need to use the Tracking Provider in a custom next app file.

    // pages/_app.js

    import { TrackingProvider } from './contexts/tracking'

    function App({ Component, pageProps }) {
        return <AllYourOtherProviders>
                    <TrackingProvider>
                        <Component {...pageProps} />
                    </TrackingProvider>
            </AllYourOtherProviders>
    }
Enter fullscreen mode Exit fullscreen mode

Now that the rest of our App has access to our TrackingProvider, we can start to initialize Google Analytics and track all page views that occur within the app.

    // contexts/trackers.jsx

    import React, { useState, useEffect } from  'react';
    import Router from 'next/router'

    const TrackingID = 'UA-XXXXXXXX-X';
    const TrackingContext = React.createContext();

    function TrackingProvider(props) {
        // if the userId is passed in, we'll need to keep track of any
        // login/logout changes
        // another method is presented below, this will vary depending
        // on your authentication method
        const { userIdThatMightChange } = props

        // we create a default state to keep track of whether GA
        // has been initialized, if we're tracking a unique user,
        // and to hold all of our trackers

        const [analytics, setAnalytics] = useState({
            isInitialized: false,
            hasUser: false,
            trackers: ['myDefaultTracker']
        })

        // We create a function handle all route changes that occur
        // and track a users movements across pages in our app

        const handleRouteChange = url  => {
            ReactGA.set({ page:  url }, analytics.trackers);
            ReactGA.pageview(url, analytics.trackers);
        };

        // We only want to initialize GA on the client side
        // This will fail if you're trying to initialize server side
        // useEffect will help us handle this case as it only runs
        // client side

        useEffect(() => {
            const { isInitialized, hasUser, trackers } = analytics

            // How you detect which user is currently logged in
            // depends on the way you've set up authentication within
            // your app, the important thing is getting the userId

            const userId = getUserFromApi()

            // initialize GA with our tracking id
            // uncomment the user tracking method that works for you

            if (!isInitialized) {
                ReactGA.initialize(TrackingID, {
                    ...variousOptions,
                    gaOptions: {
                        userId: userId,
                        // userId: userIdThatMightChange
                    }
                })

                // Handle all route changes

                Router.events.on('routeChangeComplete', handleRouteChange);

                setAnalytics(prev  => ({
                    ...prev,
                    isInitialized:  true,
                    hasUser:  Boolean(userId)
                }));

                // in case we dont have the user initially,
                // we handle setting a user in our tracker

            } else if (isInitialized && !hasUser) {
                ReactGA.set({ userId }, trackers)

                setAnalytics(prev  => ({
                    ...prev,
                    hasUser:  Boolean(userId)
                }));
            }

            return () => {
                // clean up
                Router.events.off('routeChangeComplete', handleRouteChange);
            }
        }, [userIdThatMightChange])

        return <TrackingContext.Provider {...props} />
    }

    const  useTracking = () =>  React.useContext(TrackingContext);

    export { TrackingProvider, useTracking };
Enter fullscreen mode Exit fullscreen mode

If you've made it this far, great!

So far we've:

  1. Initialized Google Analytics
  2. Are tracking unique users in our App
  3. Are tracking all page changes within our app

Now what remains is:

  • [ ] logging specific events that occur within the app
  • [ ] adding multiple trackers to GA.

Adding Multiple Trackers

Advanced analytics will require multiple trackers whether for users or for your own business. Extending the GA trackers requires another trackingId as well as naming each of trackers.

    // contexts/trackers.js

    function TrackingProvider(props) {
        ...
        // We'll define our addTracker function before useEffect
        const addTracker = (trackerId, trackerName) => {
          if (analytics.isInitialized) {
            ReactGA.addTrackers([
                {
                    trackingId:  trackerId,
                    gaOptions: {
                        name:  trackerName
                    }
                }
            ]);
            setAnalytics((prev) => ({
                ...prev,
                trackers: [...prev.trackers, trackerName]
            }))
          }
        }
        const removeTracker = (trackerName) => {
            if (analytics.isInitialized) {
            setAnalytics((prev) => ({
                ...prev,
                trackers: prev.trackers.filter((tracker) => tracker !== trackerName)
            }))
            }
        }
        useEffect(() => {
            ...
        })
        ...
        return <TrackingContext.Provider 
            value={{ addTracker, removeTracker }}
            {...props}
            />
    }
Enter fullscreen mode Exit fullscreen mode

Logging Events in our Application

We'll extend our TrackingContext with a logEvent method. This will allow us to have access to our tracking utility while keeping track of whether GA is initialized and the current user accessing it.

    // contexts/trackers.js

    function TrackingProvider(props) {
        ...
        // We'll define our logEvent function before useEffect
        const logEvent = ({ category = '', action = '', label = '' }) => {
          if (analytics.isInitialized) {
              ReactGA.event({
              category,
              action,
              label
              }, analytics.trackers)
          }
        }
        useEffect(() => {
        ...
        })
        ...
        return <TrackingContext.Provider 
            value={{ logEvent, addTracker, removeTracker }}
            {...props}
            />
    }
Enter fullscreen mode Exit fullscreen mode

ReactGA offers more information on the type of data you can send to Google Analytics. I recommend reviewing their documentation to extend it to your use case.

Using logEvent in your components

To use the tracker, we'll import our useTracking method into the components where specific events occur. This example component submits a form (use your imagination for the rest of the code)

    // components/form.jsx

    import { useTracking } from 'contexts/tracker' 

    function Form(props) {
        const { logEvent } = useTracking()
        return <form onSubmit={() => {
                // code that does something to submit data
                logEvent({
                    category: 'ExampleCategory',
                    action: 'Submitted Data',
                    label: 'Special Label'
                    })
            }}>
                <button type="submit">Submit</button>
            </form>
    }
Enter fullscreen mode Exit fullscreen mode

Thanks for following along!

This setup is a little more than what's required but extending tracking to use a unique user and localizing event logging greatly benefits the scalability of your application and decreases logging complexity.

Extend the tracking context even more by integrating a Facebook Pixel and other tracking utilities like Segment and MixPanel!

Check out the original posting here and follow me on twitter!

Top comments (3)

Collapse
sonyarianto profile image
Sony AK

Nice article, but now I am using GA4, is there any difference on implementation for GA4?

Collapse
markkdev profile image
Mark Khuzam Author

Hi Sony,

This implementation focuses more on storing the user data in state and allowing you to make logging scalable throughout your entire application.

Using GA4 would only require extending the logging functions to accept data being passed in.

You can initialize GA4 in the same place as the tutorial, then when you have user data you can begin logging it to the GA4 api

Collapse
sonyarianto profile image
Sony AK

thank you :)

👋 Welcome new DEV members in our Welcome Thread

Say hello to the newest members of DEV.