Mark Khuzam
Dec 15, 2020
Setting Up Google Analytics with React Context in Next.js
Google Analytics is a free tool offered by Google to assist you in analyzing 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 Google 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
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 };
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 a 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>
);
}
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 };
If you've made it this far, great!
So far we've:
- Initialized Google Analytics
- Are tracking unique users in our App
- 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 wether 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}
/>
}
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}
/>
}
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>
);
}
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!