Sitemap
Radzion

Radzion

Crafting increaser.org to turn your goals into reality.

2 min readAug 26, 2023

--

✨ Watch on YouTube | 🐙 GitHub | 🎮 Demo

Let’s add Amplitude analytics to a NextJS app!

Get Radzion’s stories in your inbox

Join Medium for free to get updates from this writer.

In the example below, we will track page views by calling the trackEvent method on the analytics instance from the useEffect hook:

function MyApp({ Component, pageProps }: AppProps) {
const { pathname } = router
useEffect(() => {
analytics.trackEvent("Visit page", { pathname })
}, [pathname])

return <p>my app</p>
}

export default MyApp

This is how you create the analytics instance for your app:

import { isProduction } from "shared"
import { shouldBeDefined } from "@increaser/utils/shouldBeDefined"
import { AmplitudeAnalytics } from "@increaser/ui/analytics/AmplitudeAnalytics"
import { LocalAnalytics } from "@increaser/ui/analytics/LocalAnalytics"

export const analytics = isProduction
? new AmplitudeAnalytics(
shouldBeDefined(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY)
)
: new LocalAnalytics()

First, we check if the environment is set to production. We want to avoid sending analytics events in development or testing environments.

export const isProduction = process.env.NODE_ENV === "production"

The shouldBeDefined function will throw an error if the value is undefined. This is useful to ensure we never forget to set the environment variable.

export function shouldBeDefined<T>(
value: T | undefined,
valueName: string = "value"
): T {
if (value === undefined) {
throw new Error(`${valueName} is undefined`)
}

return value
}

Both AmplitudeAnalytics and LocalAnalytics adhere to the same interface, which should include:

  • setUser: used to associate the user with the analytics events. This would only be used if your app or website has authenticated users.
  • trackEvent: used to track events. Extra info can be passed into the second argument, such as the name of the page in our previous example.
export interface Analytics {
setUser: (id: string) => void
trackEvent: (name: string, data?: Record<string, any>) => void
}

Here’s how you would implement AmplitudeAnalytics:

import { Analytics } from "./Analytics"
import * as amplitude from "@amplitude/analytics-browser"

export class AmplitudeAnalytics implements Analytics {
apiKey: string
isInitialized: boolean = false

constructor(apiKey: string) {
this.apiKey = apiKey
}

private initialize() {
if (!this.isInitialized) {
amplitude.init(this.apiKey)
this.isInitialized = true
}
}

setUser(id: string) {
this.initialize()
amplitude.setUserId(id)
}

trackEvent(name: string, data?: Record<string, any>) {
this.initialize()
amplitude.track(name, data)
}
}

It requires the @amplitude/analytics-browser package to be installed and an apiKey to be passed as a constructor argument. We don't initialize Amplitude immediately; instead, we only do so the first time setUser or trackEvent is called. This ensures that requests are only sent to Amplitude upon tracking.

Here’s how you can implement LocalAnalytics:

import { Analytics } from "./Analytics"

export class LocalAnalytics implements Analytics {
constructor() {
console.log("Initialize local analytics")
}

setUser(id: string) {
console.log("Set user for analytics: ", id)
}

trackEvent(name: string, data?: Record<string, any>) {
console.log("Track event: ", name, data)
}
}

This merely logs the events to the console. It’s useful for debugging events during development.

--

--

Radzion
Radzion

Written by Radzion

Crafting increaser.org to turn your goals into reality.