Integrating Amplitude Analytics with NextJS App: A Comprehensive Guide
Tags: nextjs, amplitudeanalytics
Published: 2023-08-26 13:18:05

✨ [**Watch on YouTube**](https://youtu.be/hAhq0uH5Kcc)  |  🐙 [GitHub](https://github.com/radzionc/reactkit/blob/main/app/pages/_app.tsx)  |  🎮 [Demo](https://reactkit.radzion.com)

Let's add Amplitude analytics to a NextJS app!

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

```tsx
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:

```tsx
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.

```tsx
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.

```ts
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.

```tsx
export interface Analytics {
  setUser: (id: string) => void
  trackEvent: (name: string, data?: Record<string, any>) => void
}
```

Here's how you would implement `AmplitudeAnalytics`:

```tsx
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`:

```tsx
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.