# Quickstart: Next.js > Install the OakData SDK in a Next.js App Router project using the instrumentation-client.ts pattern. Source: https://oakdata.co/docs/quickstart/nextjs --- OakData ships as `oakdata-js`, a zero-config browser tracker. On Next.js (App Router, 15.1+) the cleanest install point is `instrumentation-client.ts` - Next.js runs it once on the client before your app hydrates, so tracking starts immediately. In a hurry? Paste this prompt into Claude Code, Cursor, or any agent working in your repo, and give it your public key when asked: **Prompt for a coding agent:** ``` Set up OakData product analytics in this Next.js App Router project using the oakdata-js SDK. 1. Install the package: run "npm install oakdata-js". 2. Create or edit .env.local at the project root and add: NEXT_PUBLIC_OAK_KEY= NEXT_PUBLIC_OAK_HOST=https://oakdata.co Ask me for the key value if you don't already have it. 3. Create instrumentation-client.ts at the project root (next to app/, not inside it). Import oakdata-js and call oak.init(process.env.NEXT_PUBLIC_OAK_KEY!, { api_host: process.env.NEXT_PUBLIC_OAK_HOST }). 4. In my sign-in handler, right after auth returns the user, call oak.identify(user.id, { email: user.email }). In my sign-out handler, call oak.reset(). 5. If I sign in with OAuth/SSO (e.g. Google), also identify on first load from instrumentation-client.ts: read the current session and, if there is a signed-in user whose id is not already oak.getDistinctId(), call oak.identify(user.id, { email }). Use my existing auth library. Don't add any other analytics providers or wrapper components. Show me the changes before applying them. ``` Prefer to wire it up yourself? The steps below are exactly what that prompt does. ## 1. Install the package **npm** ```bash npm install oakdata-js ``` **pnpm** ```bash pnpm add oakdata-js ``` **yarn** ```bash yarn add oakdata-js ``` **bun** ```bash bun add oakdata-js ``` ## 2. Add your keys Create (or edit) `.env.local` at your project root. The `NEXT_PUBLIC_` prefix is required - these values run in the browser. Use your **public** key (`oak_pub_…`); never put a secret key here. **.env.local** ```bash NEXT_PUBLIC_OAK_KEY=oak_pub_xxxxxxxxxxxxxxxxxxxxxxxx NEXT_PUBLIC_OAK_HOST=https://oakdata.co ``` ## 3. Initialize the tracker Create `instrumentation-client.ts` at your project root (next to `app/`, not inside it): **instrumentation-client.ts** ```ts import oak from 'oakdata-js' oak.init(process.env.NEXT_PUBLIC_OAK_KEY!, { api_host: process.env.NEXT_PUBLIC_OAK_HOST, }) ``` That's it. Pageviews fire on load and on every client-side route change, and clicks, form submits, and other interactions are [autocaptured](https://oakdata.co/docs/sdk/autocapture). > **Older Next.js?** On versions without `instrumentation-client.ts` support, call `oak.init()` from a small `"use client"` component in your root layout, inside a `useEffect` with an empty dependency array. Calling `init` more than once is a no-op, so it's safe. ## 4. Identify signed-in users Add one line to your **sign-in handler**, right where auth returns the user. `oak.identify` links everything they did before signing in to the identified profile. Pass their id plus any traits (email, name, plan…) you want in the dashboard. **sign-in-handler.ts** ```ts import oak from 'oakdata-js' async function onSignIn(email, password) { const { user } = await auth.signIn(email, password) // 👇 the only line you add oak.identify(user.id, { email: user.email, name: user.name }) } ``` And add `oak.reset()` to your **sign-out handler** so the next person on that browser starts fresh: **sign-out-handler.ts** ```ts async function onSignOut() { await auth.signOut() oak.reset() } ``` That's the whole setup - no extra components or providers. Calls made before `init()` are queued and replayed, so ordering never matters. See [identity resolution](https://oakdata.co/docs/concepts/identity) for how anonymous and identified activity are stitched together. > **Using OAuth / SSO?** With social or SSO login the user returns through a server redirect, so there's no client handler for `oak.identify`. Instead, identify from `instrumentation-client.ts` - right after `oak.init()`, read the session and identify the user if they aren't already. The distinct-id guard makes it a no-op for anyone the sign-in handler already covered. **instrumentation-client.ts** ```ts import oak from 'oakdata-js' // your auth client - e.g. Supabase, Auth.js, Clerk import { getSession } from '@/lib/auth' oak.init(process.env.NEXT_PUBLIC_OAK_KEY!, { api_host: process.env.NEXT_PUBLIC_OAK_HOST, }) // OAuth/SSO users land here after the redirect - identify them once. getSession().then((session) => { const user = session?.user if (!user || oak.getDistinctId() === user.id) return oak.identify(user.id, { email: user.email }) }) ``` ## 5. Track custom events Beyond autocapture, send your own events with `capture`: **checkout.ts** ```ts import oak from 'oakdata-js' oak.capture('signup_completed', { plan: 'pro' }) ``` ## 6. See it working Load a page, then open **Live** in your [dashboard](https://oakdata.co/app/projects). Your visit shows up within a second or two, with its page, location, and device. Known people appear by name; anonymous humans get a friendly alias; bots are labelled as crawlers - never disguised as people. Live · now AP Amber Panda /pricing · United States2s Googlebot /blog · crawler9sBots shown as crawlers The Live feed as visits arrive. A human alias sits alongside a crawler shown honestly as Googlebot. The [SDK reference](https://oakdata.co/docs/sdk/reference) covers every method and init option.