How I Use TypeScript to Track Google Analytics Events with Gatsby

4 min read

Context

I’m going to show you how I use TypeScript to improve Google Analytics events tracking. The beauty of using TypeScript is we can use the enum type to create discrete options to choose from. This should help us make our analytics more consistent.

The examples we’ll look at involve the Gatsby framework.

npm install gatsby-plugin-google-analytics

Learning how to use Google Analytics or Gatsby is beyond the scope of this article. For a general tutorial on setting up Google Analytics with Gatsby, check out this video by Guiding Digital.

Event Tracking with TypeScript

There are three main properties we want to set when tracking an event.

These are how I think of each event property.

  • Category. What type of thing was interacted with?
  • Action. How was the thing interacted with?
  • Label. Which specific thing was interacted with?

In my project, I create an analytics.ts to store information about event tracking.

I like to use the TypeScript enum for each event property. This lets me have discrete properties that I can use throughout my site. I find this approach more maintainable than using ad-hoc strings.

// ❌ Bad
// Ad-hoc strings could lead to inconsistencies in analytics bucketing
trackCustomEvent({
  category: 'Newsletter',
  action: 'Subscribe',
});

// ✅ Good
// Discrete options to choose makes bucketing analytics more maintainable
trackCustomEvent({
  category: AnalyticsCategory.Newsletter,
  action: AnalyticsAction.Subscribe,
});

I define the following enum types to give me a discrete set of options that I can use throughout my site. With these types, I can create my own custom AnalyticsEvent interface to model an event.

analytics.ts
export enum AnalyticsCategory {
  Themes = 'Themes',
  Newsletter = 'Newsletter',
  Blogs = 'Blogs',
  Feedback = 'Feedback',
  Contact = 'Contact',
}

export enum AnalyticsAction {
  Click = 'Click',
  Subscribe = 'Subscribe',
  Switch = 'Switch',
  SendEmail = 'Send Email',
  Like = 'Liked',
  Dislike = 'Dislike',
  Dismiss = 'Dismiss',
}

export enum AnalyticsLabel {
  Light = 'Light',
  Dark = 'Dark',
  Green = 'Green',
  Blue = 'Blue',
  Red = 'Red',
}

export interface AnalyticsEvent {
  category: AnalyticsCategory;
  action: AnalyticsAction;
  label?: AnalyticsLabel | string;
}

I let the label property also accept a string because sometimes I need to label something dynamically.

For example, here I let the label be the page the user was on when they interacted with some Button component.

Button.tsx
import {useLocation} from '@reach/router';
import {trackCustomEvent} from 'gatsby-plugin-google-analytics';
import {
  AnalyticsAction,
  AnalyticsCategory,
  AnalyticsEvent,
} from '@utils/analytics';

function Button() {
  const {pathname} = useLocation();
  return (
    <button
      type="button"
      onClick={() => {
        trackCustomEvent({
          category: AnalayticsCategory.Feedback,
          action: AnalyticsAction.SentEmail,
          label: pathname,
        });
      }}
    >
      Click
    </button>
  );
}

I also create a thin wrapper over the trackCustomEvent function exported by gatsby-plugin-google-analytics.

The plugin supports TypeScript, but it doesn’t support the custom AnalyticsEvent interface that I defined.

Making this wrapper lets me use my AnalyticsEvent type instead of the CustomEventArgs interface that is exported by gatsby-plugin-google-analytics.

analytics.ts
import {trackCustomEvent} from 'gatsby-plugin-google-analytics';

// Thin wrapper to get TypeScript support
export function track(event: AnalyticsEvent) {
  trackCustomEvent(event);
}

With that change, we can refactor our Button component ever so slightly.

Button.tsx
import {useLocation} from '@reach/router';
import {
  AnalyticsAction,
  AnalyticsCategory,
  AnalyticsEvent,
  track,
} from '@utils/analytics';

function Button() {
  const {pathname} = useLocation();
  return (
    <button
      type="button"
      onClick={() => {
        const event: AnalyticsEvent = {
          category: AnalayticsCategory.Feedback,
          action: AnalyticsAction.SentEmail,
          label: pathname,
        };
        track(event);
      }}
    >
      Click
    </button>
  );
}

That’s all there is to it. TypeScript makes it easy to choose from a selection of discrete event types. I like this approach because it makes your analytics more consistent and helps you collect better data on how users are interacting with your site.