Headstream

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.

See which marketing makes money.

Get started free