# Next.js Installation Guide

Source: https://amplitude.com/docs/sdks/frameworks/nextjs-installation-guide

---

On this page

- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Client-side setup](#client-side-setup)
- [Amplitude initialization](#amplitude-initialization)
- [App Router (Next.js 13+)](#app-router-nextjs-13)
- [Pages router (legacy)](#pages-router-legacy)
- [Using Amplitude in components](#using-amplitude-in-components)
- [Server-side setup](#server-side-setup)
- [Server components and API routes](#server-components-and-api-routes)
- [Best practices](#best-practices)
- [Environment variables](#environment-variables)
- [User identification](#user-identification)
- [Automatic page view tracking](#automatic-page-view-tracking)
- [Session Replay integration](#session-replay-integration)
- [Use Autocapture and custom events](#use-autocapture-and-custom-events)
- [Visual Labeling for Next.js](#visual-labeling-for-nextjs)
- [Middleware integration](#middleware-integration)
- [Session management](#session-management)
- [TypeScript support](#typescript-support)
- [Testing](#testing)
- [Debugging](#debugging)
- [Common issues and solutions](#common-issues-and-solutions)
- [Window is not defined](#window-is-not-defined)
- [Duplicate events](#duplicate-events)
- [Missing user context](#missing-user-context)
- [Additional resources](#additional-resources)

# Next.js Installation Guide

This guide covers installing and setting up Amplitude's Browser SDK in Next.js applications. It includes both client-side and server-side configurations.

## Prerequisites

- Next.js 13.0 or higher.
- Node.js 16.8 or higher.
- An Amplitude account with an API key.

## Installation

Unified SDK Recommended

The [Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) provides access to Analytics, Experiment, and Session Replay in a single package. Amplitude recommends this approach for new Next.js projects.

Install the Amplitude SDK using your package manager:

bash

```
# Recommended: Install Unified SDK (includes Analytics, Experiment, Session Replay)
npm install @amplitude/unified

# Or install Analytics SDK only
npm install @amplitude/analytics-browser

## Client-side setup

### Amplitude initialization

Create an Amplitude module to initialize the SDK on the client side.

typescript

// amplitude.ts
"use client";

import * as amplitude from "@amplitude/unified";

async function initAmplitude() {
  await amplitude.initAll(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY!, {
    analytics: {
      autocapture: true,
    },
  });
}

if (typeof window !== "undefined") {
  initAmplitude();

export const Amplitude = () => null;

export default amplitude;

### App Router (Next.js 13+)

Import and add the component to your root layout.

// app/layout.tsx
import { Amplitude } from "@/amplitude";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <Amplitude />
      <body>
        {children}
      </body>
    </html>
  );

### Pages router (legacy)

For the Pages Router, initialize Amplitude in `_app.tsx`.

// pages/_app.tsx
import '@/amplitude';
import type { AppProps } from 'next/app';

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;

export default MyApp;

### Using Amplitude in components

// components/TrackingButton.tsx

import amplitude from "@/amplitude";

export function TrackingButton() {
  const handleClick = () => {
    amplitude.track("Button Clicked", {
      buttonName: "CTA Button",
      page: window.location.pathname,
      timestamp: new Date().toISOString(),
  };

  return <button onClick={handleClick}>Click Me</button>;

## Server-side setup

### Server components and API routes

For server-side tracking, use the Node.js SDK instead of the Browser SDK.

npm install @amplitude/analytics-node

Create a server-side Amplitude client.

// lib/amplitude-server.ts
import {
  init,
  track,
  identify,
  flush,
  Identify,
} from "@amplitude/analytics-node";

// Initialize once
const amplitudeServer = init(process.env.AMPLITUDE_API_KEY!);

export async function trackServerEvent(
  eventName: string,
  userId?: string,
  eventProperties?: Record<string, any>,
) {
  try {
    track(eventName, eventProperties, {
      user_id: userId,

    // Ensure events are sent before function ends
    await flush().promise;
  } catch (error) {
    console.error("Failed to track server event:", error);

export async function identifyServerUser(
  userId: string,
  userProperties?: Record<string, any>,
    const identifyObj = new Identify();

    // Set user properties if provided
    if (userProperties) {
      Object.entries(userProperties).forEach(([key, value]) => {
        identifyObj.set(key, value);

    identify(identifyObj, {

    console.error("Failed to identify user:", error);

Add the server-side client to API routes.

// app/api/track/route.ts (App Router)
import { NextRequest, NextResponse } from "next/server";
import { trackServerEvent } from "@/lib/amplitude-server";

export async function POST(request: NextRequest) {
  const body = await request.json();
  const { eventName, userId, properties } = body;

  await trackServerEvent(eventName, userId, properties);

  return NextResponse.json({ success: true });

// pages/api/track.ts (Pages Router)
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method not allowed" });

  const { eventName, userId, properties } = req.body;

  res.status(200).json({ success: true });

## Best practices

### Environment variables

Store your API keys in environment variables:

# .env.local
NEXT_PUBLIC_AMPLITUDE_API_KEY=your_client_api_key
AMPLITUDE_API_KEY=your_server_api_key

Security Note

Only use `NEXT_PUBLIC_` prefix for client-side API keys. Server-side API keys should never be exposed to the client.

### User identification

Identify users after authentication:

// After successful login
const handleLogin = async (email: string, userId: string) => {
  // Client-side identification
    amplitude.setUserId(userId);
    amplitude.identify(
      new amplitude.Identify()
        .set("email", email)
        .set("loginTime", new Date().toISOString()),

  // Server-side identification (if needed)
  await fetch("/api/identify", {
    method: "POST",
    body: JSON.stringify({ userId, email }),

### Automatic page view tracking

Amplitude's autocapture feature automatically tracks page views in Next.js applications.

// Page views are automatically tracked when you enable autocapture
amplitude.initAll(apiKey, {
    autocapture: {
      pageViews: true, // Automatically tracks route changes

Page View Configuration

Autocapture intelligently detects Next.js route changes and tracks them as page views. For advanced configuration, review [Track page views](/docs/sdks/analytics/browser/browser-sdk-2#track-page-views).

### Session Replay integration

Capture user sessions to understand behavior and debug issues:

// Enable Session Replay with the Unified SDK

      sessions: true,
      pageViews: true,
      formInteractions: true,
  sessionReplay: {
    sampleRate: 0.5, // Sample 50% of sessions

Session Replay

Session Replay provides visual playback of user sessions. Learn more in the [Session Replay documentation](/docs/session-replay).

### Use Autocapture and custom events

Use Amplitude's autocapture to automatically track common interactions:

// Enable comprehensive autocapture
      fileDownloads: true,
      elementInteractions: true, // Tracks clicks on buttons, links, and more.

For business-specific events that autocapture doesn't cover:

// Usage for custom business events

export function ProductCard({ product }: { product: Product }) {
  const handleAddToCart = () => {
    amplitude.track('Product Added to Cart', {
      productId: product.id,
      productName: product.name,
      price: product.price,
      category: product.category,
    // Add to cart logic

    <div>
      <button onClick={handleAddToCart}>Add to Cart</button>
    </div>

Autocapture vs Custom Events

Autocapture handles standard interactions like clicks, form submissions, and page views. Use custom events for business-specific actions like "Product Added to Cart" or "Subscription Upgraded."

### Visual Labeling for Next.js

Use Amplitude's Visual Labeling to tag elements directly in your browser without code changes:

// Visual Labeling works automatically with autocapture enabled
      elementInteractions: true, // Required for Visual Labeling

Visual Labeling

Visit your Next.js app with the [Amplitude Chrome Extension](https://chromewebstore.google.com/detail/amplitude-event-explorer/acehfjhnmhbmgkedjmjlobpgdicnhkbp) to view the fired events in the page.

### Middleware integration

Track server-side events using Next.js middleware:

// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  // Track API requests
  if (request.nextUrl.pathname.startsWith("/api")) {
    // Log to server-side analytics
    console.log("API Request:", {
      path: request.nextUrl.pathname,
      method: request.method,

  return NextResponse.next();

export const config = {
  matcher: ["/api/:path*"],

### Session management

Set the user ID on login and reset it on logout:

// utils/amplitude-session.ts

export function handleUserSession() {
  // On login
  const onLogin = (userId: string, userProperties?: Record<string, any>) => {
      const identify = new amplitude.Identify();
        identify.set(key, value);
      amplitude.identify(identify);

  // On logout
  const onLogout = () => {
    amplitude.setUserId(undefined);
    amplitude.reset();

  return { onLogin, onLogout };

### TypeScript support

To create type-safe event tracking, use [Ampli](/docs/sdks/ampli/migrate-to-ampli).

### Testing

Mock Amplitude in tests:

// __mocks__/amplitude.ts
export const mockAmplitude = {
  initAll: jest.fn(),
  track: jest.fn(),
  identify: jest.fn(),
  setUserId: jest.fn(),
  reset: jest.fn(),

jest.mock('@amplitude/unified', () => mockAmplitude);

// In your tests
import { render, fireEvent } from '@testing-library/react';
import { TrackingButton } from '@/components/TrackingButton';
import { mockAmplitude } from '@/__mocks__/amplitude';

describe('TrackingButton', () => {
  it('tracks click event', () => {
    const { getByText } = render(<TrackingButton />);
    fireEvent.click(getByText('Click Me'));

    expect(mockAmplitude.track).toHaveBeenCalledWith(
      'Button Clicked',
      expect.objectContaining({
        buttonName: 'CTA Button',
      })

## Debugging

Enable debug mode during development:

// Development configuration
    logLevel: amplitude.Types.LogLevel.Debug,
    minIdLength: 1, // Allow shorter IDs in development
    serverUrl: process.env.NEXT_PUBLIC_AMPLITUDE_SERVER_URL, // Custom server URL if needed

Check browser console for Amplitude logs:

- Event tracking confirmation.
- Configuration issues.
- Network request status.

## Common issues and solutions

### Window is not defined

Always check for browser environment before using Browser SDK:

  // Browser-only code

### Duplicate events

Ensure you initialize the SDK one time using React hooks:

useEffect(() => {
  // Initialization code
}, []); // Empty dependency array

### Missing user context

Set the user ID after authentication and clear it on logout:

// After auth

## Additional resources

- [Browser SDK 2 Documentation](/docs/sdks/analytics/browser/browser-sdk-2)
- [Next.js Documentation](https://nextjs.org/docs)
- [Amplitude HTTP API Reference](/docs/apis/analytics/http-v2)
- [TypeScript SDK GitHub Repository](https://github.com/amplitude/Amplitude-TypeScript)

Was this helpful?

<!--$-->

<!--/$-->
