Next.js
Install Clamp in a Next.js app. App Router, Pages Router, server-side events from Route Handlers, queryable by your AI agent via MCP.
Install
npm install @clamp-sh/analyticsApp Router
Add the Analytics component to your root layout.tsx. It’s a Client Component; render it as a sibling of {children}.
import { Analytics } from "@clamp-sh/analytics/react"
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics projectId="proj_xxx" />
</body>
</html>
)
}Pageviews — including client-side navigations via next/link and router.push() — are tracked automatically.
Pages Router
Add the component to _app.tsx:
import type { AppProps } from "next/app"
import { Analytics } from "@clamp-sh/analytics/react"
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<Analytics projectId="proj_xxx" />
</>
)
}Track custom events
Import track from the main SDK entry (not /react) and call it from any Client Component:
"use client"
import { track } from "@clamp-sh/analytics"
export function SignupButton() {
return (
<button onClick={() => track("signup_started", { plan: "pro" })}>
Sign up
</button>
)
}Properties must be flat string key-value pairs. See Events for limits and schema.
Server-side events
From a Route Handler, Server Action, or API Route, use the /server entry with an API key:
import { init, track } from "@clamp-sh/analytics/server"
init({ projectId: "proj_xxx", apiKey: process.env.CLAMP_API_KEY! })
export async function POST(req: Request) {
const { anonymousId, email } = await req.json()
await track("signup_completed", {
anonymousId,
properties: { email_domain: email.split("@")[1] },
})
return Response.json({ ok: true })
}Pass anonymousId from the browser (via getAnonymousId()) to link server events to the visitor session.
Verify
- Deploy or open your app at a non-localhost URL (localhost is skipped by design).
- Navigate between a few pages.
- In your Clamp dashboard, pageviews should appear within ~5 seconds.
- DevTools → Network → filter for
e/batchto confirm requests are sending.
Gotcha
App Router: place <Analytics /> in the root app/layout.tsx, not in a nested layout or a route group layout. The root layout is stable across navigations, so tracking initializes once. Nested layouts can remount when routing between segments, which re-runs init and can double-count sessions.