Next.js
Integrate vTilt with Next.js — App Router and Pages Router, npm package or inline script, client + server-side tracking with the Browser and Node SDKs.
vTilt works in any Next.js app (App Router or Pages Router) and across both runtimes. The Browser SDK handles client-side analytics, autocapture, and session recording; the Node SDK lets you emit events from server actions, route handlers, and middleware.
#1. Add your environment variables
Put your token and ingest origin in .env.local. Anything read in the browser must be prefixed NEXT_PUBLIC_.
NEXT_PUBLIC_VTILT_TOKEN=YOUR_PROJECT_TOKEN
NEXT_PUBLIC_VTILT_HOST=https://www.vtilt.com
VTILT_TRACKER_TOKEN=YOUR_PROJECT_TOKEN#2. Install & initialise
Choose how you load the Browser SDK. npm gives you typed imports and bundling; the inline script drops a stub in your layout with no package to install. Both expose the same window.vt global — the rest of this guide is identical either way.
Install the package:
npm install @v-tilt/browserCreate a client component that calls vt.init() once, then render it in the root layout (App Router):
// app/providers/vtilt-provider.tsx
'use client'
import { useEffect } from 'react'
import { vt } from '@v-tilt/browser'
export function VTiltProvider() {
useEffect(() => {
vt.init(process.env.NEXT_PUBLIC_VTILT_TOKEN!, {
api_host: process.env.NEXT_PUBLIC_VTILT_HOST,
autocapture: true,
capture_pageview: true,
capture_pageleave: true,
})
}, [])
return null
}// app/layout.tsx
import { VTiltProvider } from './providers/vtilt-provider'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<VTiltProvider />
{children}
</body>
</html>
)
}For the Pages Router, drop the same vt.init() into a useEffect in pages/_app.tsx instead.
#3. Track route changes
App Router client navigation goes through the History API, which the SDK already patches (pushState / replaceState), so SPA pageviews fire automatically. If you set capture_pageview: false, emit them yourself from a usePathname() effect.
#4. Identify users
Call vt.identify(userId, properties) whenever the page loads for an authenticated user — not just on login. The most common integration bug is calling identify() only inside the login submit handler, which leaves returning logged-in visitors anonymous.
'use client'
import { useEffect } from 'react'
import { vt } from '@v-tilt/browser'
export function IdentifyOnMount({
user,
}: {
user: { id: string; email: string } | null
}) {
useEffect(() => {
if (user) vt.identify(user.id, { email: user.email })
}, [user])
return null
}See Identify & alias for the full model.
#5. Server-side events with the Node SDK
For events from server actions, route handlers, and middleware, install the Node SDK:
npm install @v-tilt/nodeConstruct one client per process and await shutdown() before serverless functions exit:
// app/api/checkout/route.ts
import { NextResponse } from 'next/server'
import { VTiltNode } from '@v-tilt/node'
const vtilt = new VTiltNode(process.env.VTILT_TRACKER_TOKEN!, {
host: process.env.NEXT_PUBLIC_VTILT_HOST,
})
export async function POST(req: Request) {
const { userId, amount } = await req.json()
vtilt.capture({
distinctId: userId,
event: 'purchase_completed',
properties: { amount, source: 'next-api' },
})
await vtilt.shutdown()
return NextResponse.json({ ok: true })
}#Next steps
- Identify & alias — the full identification model.
- Reverse proxy — route SDK traffic through your own domain.
- Node SDK / Capture, identify & alias — server-side event shapes.
- Event forwarding — fan events out to GA4, Meta CAPI, PostHog.