This guide covers the installation and setup of Amplitude's Browser SDK in Next.js applications and includes instructions for both client-side and server-side configurations.
Install the Amplitude SDK using your package manager:
# Recommended: Install Unified SDK (includes Analytics, Experiment, Session Replay)
npm install @amplitude/unified
# Or install Analytics SDK only
npm install @amplitude/analytics-browser
# Recommended: Install Unified SDK (includes Analytics, Experiment, Session Replay)
yarn add @amplitude/unified
# Or install Analytics SDK only
yarn add @amplitude/analytics-browser
# Recommended: Install Unified SDK (includes Analytics, Experiment, Session Replay)
pnpm add @amplitude/unified
# Or install Analytics SDK only
pnpm add @amplitude/analytics-browser
Follow the instructions in this section to configure Amplitude on the client.
Create an Amplitude module to initialize the SDK on the client side.
// 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;
// amplitude.ts
"use client";
import * as amplitude from "@amplitude/analytics-browser";
async function initAmplitude() {
await amplitude.init(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY!, undefined, {
autocapture: true,
}).promise;
}
if (typeof window !== "undefined") {
initAmplitude();
}
export const Amplitude = () => null;
export default amplitude;
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>
);
}
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;
// components/TrackingButton.tsx
"use client";
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>;
}
Follow the instructions in this section to configure Amplitude on the server.
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>,
) {
try {
const identifyObj = new Identify();
// Set user properties if provided
if (userProperties) {
Object.entries(userProperties).forEach(([key, value]) => {
identifyObj.set(key, value);
});
}
identify(identifyObj, {
user_id: userId,
});
await flush().promise;
} catch (error) {
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";
import { trackServerEvent } from "@/lib/amplitude-server";
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;
await trackServerEvent(eventName, userId, properties);
res.status(200).json({ success: true });
}
Keep the following best practices in mind as you build your integration.
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
NEXT_PUBLIC_
prefix for client-side API keys. Server-side API keys should never be exposed to the client.Identify users after authentication:
// After successful login
const handleLogin = async (email: string, userId: string) => {
// Client-side identification
if (typeof window !== "undefined") {
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 }),
});
};
// After successful login
const handleLogin = async (email: string, userId: string) => {
// Client-side identification
if (typeof window !== "undefined") {
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 }),
});
};
Amplitude's autocapture feature automatically tracks page views in Next.js applications.
// Page views are automatically tracked when you enable autocapture
amplitude.initAll(apiKey, {
analytics: {
autocapture: {
pageViews: true, // Automatically tracks route changes
},
},
});
// Page views are automatically tracked when you enable autocapture
amplitude.init(apiKey, undefined, {
autocapture: {
pageViews: true, // Automatically tracks route changes
},
});
Capture user sessions to understand behavior and debug issues:
// Enable Session Replay with the Unified SDK
import * as amplitude from "@amplitude/unified";
amplitude.initAll(apiKey, {
analytics: {
autocapture: {
sessions: true,
pageViews: true,
formInteractions: true,
},
},
sessionReplay: {
sampleRate: 0.5, // Sample 50% of sessions
},
});
// Session Replay requires separate installation with Analytics SDK
import * as amplitude from "@amplitude/analytics-browser";
import { sessionReplayPlugin } from "@amplitude/plugin-session-replay-browser";
// Create and Install Session Replay Plugin
const sessionReplayTracking = sessionReplayPlugin({
sampleRate: 0.5, // Sample 50% of sessions
});
amplitude.add(sessionReplayTracking);
// Initialize Analytics
amplitude.init(apiKey, undefined, {
autocapture: {
sessions: true,
pageViews: true,
formInteractions: true,
},
});
Use Amplitude's autocapture to automatically track common interactions:
// Enable comprehensive autocapture
amplitude.initAll(apiKey, {
analytics: {
autocapture: {
sessions: true,
pageViews: true,
formInteractions: true,
fileDownloads: true,
elementInteractions: true, // Tracks clicks on buttons, links, and more.
},
},
});
// Enable comprehensive autocapture
amplitude.init(apiKey, undefined, {
autocapture: {
sessions: true,
pageViews: true,
formInteractions: true,
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
"use client";
import amplitude from "@/amplitude";
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
};
return (
<div>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
);
}
Use Amplitude's Visual Labeling to tag elements directly in your browser without code changes:
// Visual Labeling works automatically with autocapture enabled
amplitude.initAll(apiKey, {
analytics: {
autocapture: {
elementInteractions: true, // Required for Visual Labeling
},
},
});
// Visual Labeling works automatically with autocapture enabled
amplitude.init(apiKey, undefined, {
autocapture: {
elementInteractions: true, // Required for Visual Labeling
},
});
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,
timestamp: new Date().toISOString(),
});
}
return NextResponse.next();
}
export const config = {
matcher: ["/api/:path*"],
};
Ensure you handle user sessions correctly.
// utils/amplitude-session.ts
import * as amplitude from "@amplitude/unified";
export function handleUserSession() {
// On login
const onLogin = (userId: string, userProperties?: Record<string, any>) => {
amplitude.setUserId(userId);
if (userProperties) {
const identify = new amplitude.Identify();
Object.entries(userProperties).forEach(([key, value]) => {
identify.set(key, value);
});
amplitude.identify(identify);
}
};
// On logout
const onLogout = () => {
amplitude.setUserId(undefined);
amplitude.reset();
};
return { onLogin, onLogout };
}
// utils/amplitude-session.ts
import * as amplitude from "@amplitude/analytics-browser";
export function handleUserSession() {
// On login
const onLogin = (userId: string, userProperties?: Record<string, any>) => {
amplitude.setUserId(userId);
if (userProperties) {
const identify = new amplitude.Identify();
Object.entries(userProperties).forEach(([key, value]) => {
identify.set(key, value);
});
amplitude.identify(identify);
}
};
// On logout
const onLogout = () => {
amplitude.setUserId(undefined);
amplitude.reset();
};
return { onLogin, onLogout };
}
Create type-safe event tracking, use Ampli.
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',
})
);
});
});
// __mocks__/amplitude.ts
export const mockAmplitude = {
init: jest.fn(),
track: jest.fn(),
identify: jest.fn(),
setUserId: jest.fn(),
reset: jest.fn(),
};
jest.mock('@amplitude/analytics-browser', () => 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',
})
);
});
});
Enable debug mode during development:
// Development configuration
amplitude.initAll(apiKey, {
analytics: {
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
autocapture: true,
},
});
// Development configuration
amplitude.init(apiKey, undefined, {
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
autocapture: true,
});
Check browser console for Amplitude logs:
Always check for browser environment before using Browser SDK:
if (typeof window !== "undefined") {
// Browser-only code
}
Ensure you initialize the SDK one time using React hooks:
useEffect(() => {
// Initialization code
}, []); // Empty dependency array
Set the user ID after authentication and clear it on logout:
// After auth
amplitude.setUserId(userId);
// On logout
amplitude.reset();
June 12th, 2025
Need help? Contact Support
Visit Amplitude.com
Have a look at the Amplitude Blog
Learn more at Amplitude Academy
© 2025 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.