How to Integrate Analytics into a React/NextJS Application

June 27, 2024

4 min read

How to Integrate Analytics into a React/NextJS Application
Watch on YouTube

Integrating Analytics into a React Application

In this article, we'll explore a reusable method for integrating analytics into a React application. We'll use Amplitude analytics within a Next.js app as our example, but the same principles can be applied to any React app and analytics provider. All the reusable code for this article can be found in the RadzionKit repository.

In our app, we'll use two main functions to handle analytics:

  • setUser - This function sets the user ID for tracking purposes.
  • trackEvent - This function tracks events. It takes the event name and an optional data object, allowing us to pass any additional information about the event we want to track.
import { createContextHook } from "@lib/ui/state/createContextHook"
import { createContext } from "react"

export type AnalyticsContextState = {
  setUser: (id: string) => void
  trackEvent: (name: string, data?: Record<string, any>) => void
}

export const AnalyticsContext = createContext<
  AnalyticsContextState | undefined
>(undefined)

export const useAnalytics = createContextHook(
  AnalyticsContext,
  "AnalyticsContext"
)

Using React Context API for Analytics Integration

We'll use React's context API to provide these functions to our components. The decision to use the context API instead of utility functions will become clearer when we examine the PageVisitTracker component. At Increaser, we use this component to track page visits on both the website and the app. The PageVisitTracker doesn't need to know which analytics tool we're using. It's the responsibility of the app and website projects to wrap the PageVisitTracker in the appropriate context provider.

import { useAnalytics } from "@lib/analytics-ui/AnalyticsContext"
import { useRouter } from "next/router"
import { useEffect } from "react"

export const PageVisitTracker = () => {
  const { pathname } = useRouter()
  const { trackEvent } = useAnalytics()

  useEffect(() => {
    trackEvent("Visit page", { pathname })
  }, [trackEvent, pathname])

  return null
}

Local and Amplitude Analytics Providers

When running the app locally, we don't need to spam our analytics provider with events. For local development, we use the LocalAnalyticsProvider, which simply logs the events to the console.

import { ComponentWithChildrenProps } from "@lib/ui/props"
import { AnalyticsContext, AnalyticsContextState } from "./AnalyticsContext"

const localAnalytics: AnalyticsContextState = {
  setUser: (id) => {
    console.log("Set user for analytics: ", id)
  },
  trackEvent: (name, data) => {
    console.log("Track event: ", name, data)
  },
}

export const LocalAnalyticsProvider = ({
  children,
}: ComponentWithChildrenProps) => {
  return (
    <AnalyticsContext.Provider value={localAnalytics}>
      {children}
    </AnalyticsContext.Provider>
  )
}

Our AmplitudeAnalyticsProvider offers the setUser and trackEvent functions using the Amplitude analytics library. We initialize the Amplitude library with the provided API key when the component mounts.

import { useEffect } from "react"
import * as amplitude from "@amplitude/analytics-browser"
import { AnalyticsContext, AnalyticsContextState } from "./AnalyticsContext"
import { ComponentWithChildrenProps } from "@lib/ui/props"

type AmplitudeAnalyticsProviderProps = ComponentWithChildrenProps & {
  apiKey: string
}

const amplitudeAnalytics: AnalyticsContextState = {
  setUser: (id) => {
    amplitude.setUserId(id)
  },
  trackEvent: (name, data) => {
    amplitude.track(name, data)
  },
}

export const AmplitudeAnalyticsProvider = ({
  apiKey,
  children,
}: AmplitudeAnalyticsProviderProps) => {
  useEffect(() => {
    amplitude.init(apiKey)
  }, [apiKey])

  return (
    <AnalyticsContext.Provider value={amplitudeAnalytics}>
      {children}
    </AnalyticsContext.Provider>
  )
}

Integrating the Analytics Provider into a Next.js App

Now, let's see how we put it all together within our Next.js app. First, we create an AnalyticsProvider component that checks if the app is running in production. If it is, we use the AmplitudeAnalyticsProvider with the API key taken from environment variables. Otherwise, we use the LocalAnalyticsProvider.

import { shouldBeDefined } from "@lib/utils/assert/shouldBeDefined"
import { ComponentWithChildrenProps } from "@lib/ui/props"
import { AmplitudeAnalyticsProvider } from "@lib/analytics-ui/AmplitudeAnalyticsProvider"
import { LocalAnalyticsProvider } from "@lib/analytics-ui/LocalAnalyticsProvider"

export const AnalyticsProvider = ({ children }: ComponentWithChildrenProps) => {
  if (process.env.NODE_ENV === "production") {
    return (
      <AmplitudeAnalyticsProvider
        apiKey={shouldBeDefined(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY)}
      >
        {children}
      </AmplitudeAnalyticsProvider>
    )
  }

  return <LocalAnalyticsProvider>{children}</LocalAnalyticsProvider>
}

Finally in the _app.tsx file, we wrap our app with the AnalyticsProvider component and place the PageVisitTracker component inside it. This way, we can track page visits across the entire app.

import { AnalyticsProvider } from "../analytics/AnalyticsProvider"
import { PageVisitTracker } from "@lib/next-ui/PageVisitTracker"

function MyApp() {
  return (
    <AnalyticsProvider>
      <PageVisitTracker />
      // ...
    </AnalyticsProvider>
  )
}

export default MyApp