How to Add Analytics to Your Next.js App

Track signups, purchases and custom events in your Next.js app with a few lines of server-side code and GraphJSON.

JR2 min read

Pageview analytics tell you how many people showed up. They don't tell you who signed up, who upgraded, or which feature nobody touches. For that you need custom event tracking - and in a Next.js app, you can wire it up in about ten lines of code.

Log events server-side#

We recommend logging from the server rather than the browser. Two reasons: your API key stays secret (client-side code is public), and ad blockers can't drop your events. In Next.js, API routes and server actions are the natural place for this - you're already handling the signup or purchase there, so logging the event is one extra call.

Here's the whole integration - a small helper:

// lib/logEvent.ts
export async function logEvent(event: Record<string, unknown>) {
  const response = await fetch("https://api.graphjson.com/api/log", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      api_key: process.env.GRAPHJSON_API_KEY,
      collection: "product_events",
      json: JSON.stringify(event),
    }),
    signal: AbortSignal.timeout(5000),
  });

  if (!response.ok) {
    throw new Error(`GraphJSON returned ${response.status}`);
  }
}

One gotcha worth knowing: the json field must be a stringified JSON blob, not an object - hence the nested JSON.stringify. The timestamp field is optional and defaults to now (Unix seconds, not milliseconds). Set a collection explicitly so ownership, retention, and SQL stay clear.

Now use it anywhere on the server:

// app/api/signup/route.ts
import { logEvent } from "@/lib/logEvent";

export async function POST(req: Request) {
  // ...create the user
  await logEvent({ event: "signup_completed", plan: "free", source: "landing" });
  return Response.json({ ok: true });
}

A note on serverless: if you're on Vercel, don't fire-and-forget without awaiting - the function may freeze before the request completes. Await the call, use waitUntil, or put important events in a durable queue. Analytics failure should not roll back a completed signup or payment; the reliable delivery guide covers that boundary.

There's no schema to define. Add fields whenever you want - plan, source, referrer, anything - and GraphJSON's typeahead will surface them when you're building graphs.

What you can do with the data#

Once events flow in, the point-and-click visualizer turns them into graphs - signups over time, conversion by source, whatever you need. When the UI isn't expressive enough, the SQL notebooks give you raw ClickHouse SQL over your events. And if you want the numbers in front of you, pin them to a dashboard or set an alert when a key metric moves.

If you're hosted on Vercel, our one-click integration also forwards your Vercel logs to GraphJSON, which complements custom events nicely: infrastructure logs for debugging, product events for analytics.

GraphJSON is free up to 5,000 stored events, which is plenty to instrument an initial launch. See pricing for current metering details.

JR

Written by JR

Founder and builder of GraphJSON.