Next.js
Updated 2026-11-02
Setup
Copy your snippet from Project settings in the dashboard. It looks like the one below, with your own key.
App Router
Add the script to the <head> in app/layout.tsx:
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<script
defer
src="https://headstream-ingest.super-dream-7005.workers.dev/t.js"
data-key="YOUR_KEY"
/>
</head>
<body>{children}</body>
</html>
);
}
Pages Router
Add the same tag inside <Head> in pages/_document.tsx.
Check it works
Deploy, then load a page on your real domain. Your project's setup status changes once an event arrives. Events from localhost are rejected, since they don't come from a domain on your project.
Track events
Route changes count as pageviews automatically. There's no router code to add. For events, call the global from a Client Component:
"use client";
export function SignupButton() {
return (
<button onClick={() => window.headstream?.track("signup_click")}>
Sign up
</button>
);
}
TypeScript needs a declaration for the global:
declare global {
interface Window {
headstream?: {
track: (name: string, properties?: Record<string, unknown>) => void;
identify: (traits: Record<string, unknown>) => void;
};
}
}
export {};
Reduce ad blocker loss
Serve the script from your own domain with a rewrite in next.config:
async rewrites() {
return [
{
source: "/_hs/:path*",
destination: "https://headstream-ingest.super-dream-7005.workers.dev/:path*",
},
];
}
Then use the snippet from the First-party tracking card in your settings. It loads from /_hs/t.js. Copy both from there, since they hold your exact values.
If your site sets a Content Security Policy, allow the ingest host in script-src and connect-src. Next, see track events and identify people.