Skip to content

Framework integration

Session Replay for Next.js: Install and Record Routes

Add session replay to a Next.js app with next/script or the npm package: App and Pages Router, client-side route changes, masking, consent and CSP nonces.

Updated · 4 min read

To add session replay to a Next.js app, put two next/script tags in your root layout, or install @anyreplay/browser and start it from a small client component. Either way, client-side navigations in the App Router and the Pages Router are recorded as page views with no router code.

This page explains which option to pick, the one mistake that silently breaks the script version, and how to keep personal data out of recordings. The full reference is the Next.js install guide.

Why add session replay to a Next.js app

A Next.js app mixes server rendering, streaming and client-side navigation, which makes some bugs hard to describe from a ticket: a form that loses its state after a soft navigation, a button that does nothing until hydration finishes, a layout that jumps when a suspense boundary resolves. A recording shows the page as the visitor saw it, with every click and route change on a timeline, so you can watch what happened instead of guessing. The recorder is built on rrweb: it captures a DOM snapshot and the mutations that follow, never your script bodies.

How do I add session replay to Next.js?

Option 1: next/script in the root layout

This is what the dashboard's Next.js tab gives you, and it needs no dependency. Paste it into app/layout.tsx:

import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Script id="anyreplay-init" strategy="afterInteractive">{`
          window.anyreplay = window.anyreplay || function () {
            (window.anyreplay.q = window.anyreplay.q || []).push(arguments);
          };
          anyreplay('init', {
            projectKey: 'YOUR_PROJECT_KEY',
            ingestUrl: 'https://in.anyreplay.com',
            // Stop recording after this many sessions in a calendar month.
            // maxSessionsPerMonth: 10000,
            // Form fields are masked: what visitors type replays as asterisks.
            // Passwords, one-time codes and card fields are masked either way.
            // Uncomment the first line to record what visitors type, or the
            // second to hide rich-text editors too.
            // maskAllInputs: false,
            // maskAllTyping: true
          });
        `}</Script>
        <Script src="https://cdn.anyreplay.com/v1/ar.min.js?k=YOUR_PROJECT_KEY" strategy="afterInteractive" />
      </body>
    </html>
  );
}
app/layout.tsx

Option 2: the npm package in a client component

npm install @anyreplay/browser
'use client';

import { useEffect } from 'react';
import type { RecorderHandle } from '@anyreplay/browser';

let starting: Promise<RecorderHandle> | null = null;

export function getRecorder(): Promise<RecorderHandle> {
  starting ??= import('@anyreplay/browser').then(({ createRecorder }) =>
    createRecorder({ projectKey: process.env.NEXT_PUBLIC_ANYREPLAY_KEY!, requireConsent: true }),
  );
  return starting;
}

export function AnyReplayRecorder(): null {
  useEffect(() => { void getRecorder(); }, []);
  return null;
}
app/anyreplay.tsx

Render <AnyReplayRecorder /> inside <body> in your root layout. The dynamic import inside an effect keeps the recorder out of server rendering, and the module-level promise means a second mount, or Strict Mode running effects twice in development, still creates exactly one recorder. createRecorder itself does not guard against a second call, which is why it lives behind that promise.

Values prefixed with NEXT_PUBLIC_ are inlined at build time. The project key is public, so exposing it is fine, but a changed value needs a rebuild before it reaches the browser.

Does it work with the App Router and server components?

Yes. Server components render HTML; the recorder runs only in the browser and records whatever that HTML becomes, including streamed content and client components. Nothing needs to be marked 'use client' except the small recorder component in option 2. With the Pages Router, put the same Script tags in pages/_app.tsx, or mount the client component there.

Are client-side navigations recorded?

Yes. Next.js navigates with the History API, and the recorder watches pushState, replaceState and popstate. Each real URL change adds a $navigate marker with the new URL and title, and raises the session's page count. A full reload within 30 minutes continues the same session in that tab. The details, including how query string changes count, are in single-page apps.

How do I keep sensitive data out of recordings?

  • What visitors type is masked by default. Pass maskAllInputs: false to record field values, or maskAllTyping: true to mask rich-text editors as well. Only a real boolean counts.
  • Passwords, one-time codes and payment fields are masked in every configuration; no option records them. type="email" and type="tel" inputs are not on that floor: they are ordinary fields, masked by default and recorded once you opt out.
  • Add className="ar-mask" to elements that render personal data from the server, such as an account email or an address, or to a single input whose value should not be recorded.
  • Add className="ar-block" to a section that should not be recorded at all; replay shows an empty box of the same size.
  • Route URLs are recorded, so avoid putting emails or tokens into paths and query strings.

Masking happens in the browser before anything is sent. For a consent banner, use option 2 with requireConsent: true and call consent(true) on the handle when the visitor agrees, and again on each page load for visitors who already did. See consent and session replay and cookie consent.

If you set a Content Security Policy with a per-request nonce, pass the nonce to both Script elements and allow https://cdn.anyreplay.com in script-src and https://in.anyreplay.com in connect-src. The npm install needs only connect-src. See Content Security Policy.

How do I check it is working?

  1. Deploy or run the app, open it in a normal browser window and click through two or three routes.
  2. The project's setup page switches to Connected after the first accepted event.
  3. In the network panel, look for POST requests to in.anyreplay.com answered with 202.
  4. Add debug: true to the options to see in the console why the recorder is not recording, for example waiting for consent or sampled out.

Frequently asked questions

How do I add session replay to Next.js?

Paste the next/script snippet from your project into the root layout, or install @anyreplay/browser and start it once from a client component with a dynamic import. Both record client-side route changes without extra code.

Does it work with the App Router and server components?

Yes. The recorder runs in the browser and records the page that server and client components produce. It also works with the Pages Router when added to pages/_app.tsx.

Are client-side navigations recorded?

Yes. Next.js uses the History API, so each URL change is recorded as a page view with a route marker on the player timeline. No router events or integration are needed.

Does session replay affect Core Web Vitals?

The script version loads asynchronously with the afterInteractive strategy, after the page is interactive, and the recorder is about 35 KB gzipped. Recording still does work in the browser, so measure your own pages before and after installing rather than relying on a general answer.

Start free — 1 project, 500 sessions a month

Install the snippet, record a session, and watch it back in your own language.