Session replay for Next.js
Load the recorder with next/script in your root layout. It needs no npm package and works with the App Router and the Pages Router.
You need a running Replayowl server first. See Deploy to Cloudflare Workers. Below, replace replay.example.com with your server’s domain.
App Router
In app/layout.tsx:
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://replay.example.com/recorder.js"
data-endpoint="https://replay.example.com/api/ingest"
strategy="afterInteractive"
/>
</body>
</html>
);
}
Pages Router
Put the same <Script> in pages/_app.tsx, next to <Component {...pageProps} />.
Attaching the signed-in user
Call identify once you know who is signed in. window.ReplayOwl exists only after the script has loaded, so wait for it if needed:
"use client";
import { useEffect } from "react";
declare global {
interface Window { ReplayOwl?: { identify(id: string): void } }
}
export function ReplayIdentity({ userId }: { userId?: string }) {
useEffect(() => {
if (!userId) return;
const tryIdentify = () => window.ReplayOwl?.identify(userId);
if (window.ReplayOwl) tryIdentify();
else window.addEventListener("load", tryIdentify, { once: true });
}, [userId]);
return null;
}
Client-side navigation
Route changes made by the Next.js router are recorded and replayed like any other change to the page. The URL is read on full page loads only, so the session list and page searches show the page where the visit started and any pages loaded in full. See limitations.
Privacy
Inputs, emails and card numbers are masked by default. Add data-ro-mask or data-ro-block to components that show sensitive data. See Masking and privacy.