Skip to content
Docs menu · Next.js

Install — Web

Install AnyReplay in Next.js

Record a Next.js App Router site with AnyReplay: the next/script layout snippet, or a client component using the npm package that waits for consent.

There are two ways to add AnyReplay to a Next.js app using the App Router. The script version is what the dashboard's Next.js tab gives you and needs no dependency. The client component version uses the npm package and fits naturally with a consent banner. Both record route changes automatically. Masking and consent tips for this stack are collected in session replay for Next.js.

Option 1: next/script in the root layout

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

The first Script defines the queue and calls init; the second loads the recorder. Both use strategy="afterInteractive", so they run once the page is interactive and survive client-side navigation without running again.

npm install @anyreplay/browser
'use client';

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

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

/** Creates the recorder once per page load, however often it is asked for. */
export function getRecorder(): Promise<RecorderHandle> {
  starting ??= import('@anyreplay/browser').then(({ createRecorder }) =>
    createRecorder({
      projectKey: process.env.NEXT_PUBLIC_ANYREPLAY_KEY!,
      requireConsent: true,
    }),
  );
  return starting;
}

/** Call from your consent banner. */
export async function setRecordingConsent(granted: boolean): Promise<void> {
  (await getRecorder()).consent(granted);
}

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

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

Three details make this safe. The dynamic import() inside an effect keeps the recorder out of server rendering. The module-level promise means React Strict Mode's double effects in development — or the component mounting twice — still create exactly one recorder. And requireConsent: true records nothing until your banner calls setRecordingConsent(true).

import { setRecordingConsent } from '@/app/anyreplay';

<button onClick={() => { saveChoice('accepted'); void setRecordingConsent(true); }}>Accept</button>
In your consent banner

Consent is remembered by your site, not by AnyReplay: on each full page load, call setRecordingConsent(true) again if the visitor has already accepted. More patterns are in consent.

Environment variables

The project key is public, so exposing it as a NEXT_PUBLIC_ variable is fine — the name above is only a suggestion. Values prefixed with NEXT_PUBLIC_ are inlined at build time, so rebuild after changing them.

Route changes and the Pages Router

Next.js navigates with the History API, so every client-side route change is recorded as a page view with a $navigate marker — no router events needed. With the Pages Router, put the same Script tags in pages/_app.tsx, or mount the client component there. See single-page apps for what counts as a navigation.