Next.js

App Router and Pages Router, which take different imports.

Install the package

Terminal
npm i @firstrun/analytics

Add the component

App Router
app/layout.tsx
import { Analytics } from "@firstrun/analytics/next";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Analytics sourceKey="fr_xxxxxxxxxxxxxxxx" host="https://app.firstrun.app" />
      </body>
    </html>
  );
}

The component is already "use client"; the layout does not have to be. Routes come from usePathname() alone, so no Suspense boundary is needed. It is sourceKey and not key because React consumes a prop called key before the component ever sees it.

Pages Router
pages/_app.tsx
import type { AppProps } from "next/app";
import { Analytics } from "@firstrun/analytics/react";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Component {...pageProps} />
      <Analytics sourceKey="fr_xxxxxxxxxxxxxxxx" host="https://app.firstrun.app" />
    </>
  );
}

The two imports are not interchangeable. /next turns the tag's own history watching off and reports routes from usePathname() instead. next/navigation does not exist in the Pages Router, and the App Router does not move through history in a way the tag can watch, so /react there under-reports navigations.

Send your own events

Nothing is stored and nothing is sent until consent is granted.

TypeScript
import { consent, event, error, log, identify } from "@firstrun/analytics";

consent(true);
event("download_clicked", { platform: "windows" });
error(err);
log({ name: "checkout_stalled", severity: 13, attributes: { step: 3 } });
identify("u_42");

event writes at INFO and error at ERROR, both filling in the conventional attributes. log takes any event you like. Attribute values are JSON, so a number stays a number.

With consent granted the component also writes page_view, session_start, page_leave, outbound_click, file_download, form_submit and web_vital on its own. The props autoPage, autoOutbound, autoVitals, autoForms and trackLeave each turn one group off.