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:
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=<my public key, starts with oak_pub_>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 install oakdata-js2. 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.
NEXT_PUBLIC_OAK_KEY=oak_pub_xxxxxxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_OAK_HOST=https://oakdata.co3. Initialize the tracker
Create instrumentation-client.ts at your project root (next to app/, not inside it):
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.
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 initmore 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.
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:
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 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.
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:
import oak from 'oakdata-js'
oak.capture('signup_completed', { plan: 'pro' })6. See it working
Load a page, then open Live in your dashboard. 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
Amber Panda
/pricing · United States
Googlebot
/blog · crawler
The SDK reference covers every method and init option.