Documentation
Recipes

Add FounderHQ to Next.js

Install FounderHQ in a Next.js App Router app, in the browser and on the server.

Measure your Next.js app end to end: pageviews from the browser, and facts only your server knows. About 10 minutes.

Before you start

You need a FounderHQ account and two keys. In the app, open Sequences → Settings → Event API keys. Create one key for Browser/mobile (fhq_pk_...) and one for Backend (fhq_sk_...). Each full key is shown only once. Keep the secret key on your server.

This page covers the App Router. Pages Router? Call founderhq.init once in pages/_app.tsx instead, and the rest of the page still applies.

Install the browser package

npm install @founderhq/events

Initialize in a client component

The App Router renders on the server, so init belongs in a client component that you render once in your root layout.

"use client";

import { useEffect } from "react";
import { founderhq } from "@founderhq/events";

export function FounderHqAnalytics() {
  useEffect(() => {
    founderhq.init("fhq_pk_XXXX");
  }, []);
  return null;
}

founderhq is a shared client. Import it anywhere. Calling init a second time does nothing, so a hot reload is safe.

Render it once in the root layout

// app/layout.tsx
import { FounderHqAnalytics } from "./founderhq-analytics";

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

From here on, pageviews, page leaves, sessions, clicks, rage clicks, dead clicks, outbound clicks, scroll depth, and Core Web Vitals are captured for you. App Router navigations count as pageviews.

Identify the person when they sign in

founderhq.identify("user_8421", {
  email: "jane@acme.com",
  plan: "pro",
});

Pass your own stable user ID, not an email. Call founderhq.reset() when the person signs out.

Send server-truth events from route handlers

Your server knows facts the browser cannot prove: a validated signup, a plan change, a webhook you received. Send those with the Node SDK.

npm install @founderhq/events-node
// lib/analytics.ts
import { FounderHqNode } from "@founderhq/events-node";

export const events = new FounderHqNode(process.env.FOUNDERHQ_SECRET_KEY!);

Put FOUNDERHQ_SECRET_KEY=fhq_sk_XXXX in your server environment, with no NEXT_PUBLIC_ prefix. That prefix would ship the secret to the browser.

// app/api/upgrade/route.ts
import { events } from "@/lib/analytics";

export async function POST() {
  events.capture({
    contact: { externalId: "user_8421", email: "jane@acme.com" },
    event: "subscription.upgraded",
    properties: { plan: "growth", mrr: 99 },
  });
  await events.flush();
  return Response.json({ ok: true });
}

Use the same externalId you pass to identify in the browser. That is what keeps one contact instead of two.

Serverless functions exit fast, so await events.flush() before you return. Otherwise the batch dies with the function.

Attribute your revenue

Taking payments with Stripe? Follow Stripe end to end to connect the webhook and carry attribution through checkout.

Verify

  1. Run the app and load two pages.
  2. In FounderHQ, open Sequences → Events. You see two $pageview events, each with its channel.
  3. Call the route handler once.
  4. Open Sequences → Contacts and open Jane. You see subscription.upgraded with plan: growth on her timeline.

Events can take a few seconds to appear. The SDK batches them.

Troubleshoot

founderhq is not defined during a build

init ran on the server. Keep it inside the client component with "use client" at the top.

Events fire twice in development

React Strict Mode runs effects twice. init is safe to call twice, but a capture inside an effect is not. Capture on the user's action, not on render.

A second contact appeared for the same person

Your browser code and your server code use different IDs. Both must use the same stable user ID. See Identity and contacts.

Next

AI agent or LLM? Read this page as markdown

On this page