xanalyst
v0.4.0
Published
Event tracking SDK for Next.js products.
Readme
xanalyst
Event tracking SDK for Next.js (App Router) products.
Browser events go to the app's own /api/track, which forwards them to
<XANALYST_API_URL>/events/ingest after the response. Server events are
forwarded the same way. Tracking never throws and never blocks a response.
Install
pnpm add xanalystRequires Next.js 15.1+ and React 18+. next-auth is only needed for the
ready-made /api/track handler.
Configure
XANALYST_API_URL=https://... # backend base url
XANALYST_PRODUCT=my_product # product id in the backend
XANALYST_API_KEY=... # events api key from the backendIf any of these is missing, events are dropped (one warning in the server log), so local development works without them.
Events are only forwarded when NODE_ENV is production, so next dev never sends
development traffic to the backend — browser events and server events alike. To
check tracking end to end from a dev server, opt in explicitly:
XANALYST_DEV_TRACKING=1Note that next build && next start runs in production mode and does forward.
Use
- The
/api/trackroute —app/api/track/route.ts:
// apps on next-auth (JWT sessions)
export { POST } from 'xanalyst/next-auth'
// any other auth
import { createTrackHandler } from 'xanalyst/next'
export const POST = createTrackHandler({ getUserId: async (request) => /* user id or null */ null })- Page views and identify — render once in a client layout:
'use client'
import { useSession } from 'next-auth/react'
import { AnalyticsTracker } from 'xanalyst/react'
export default function Tracker() {
const { data: session } = useSession()
return <AnalyticsTracker userId={session?.user?.id} />
}- Browser events:
import { track } from 'xanalyst/client'
track('checkout_clicked', { plan: 'pro' })Events are batched: sent every 3 seconds, as soon as 10 are queued, and
immediately when the page is hidden or unloaded (full redirects and closing the
tab are covered). To send one right away, pass { immediate: true }:
track('checkout_clicked', { plan: 'pro' }, { immediate: true }). flush() is
also exported.
- Server events (route handlers, webhooks, auth callbacks):
import { trackServerEvent } from 'xanalyst/server'
trackServerEvent({ event: 'purchase_completed', userId, properties: { amount: 9 }, requestHeaders: request.headers })App version (optional)
NEXT_PUBLIC_XANALYST_APP_VERSION=1.4.0 # or a commit hashAdded to every event as context.app.version, to compare releases.
Microsoft Clarity and Google Analytics 4 (optional)
NEXT_PUBLIC_XANALYST_CLARITY_ID=... # Clarity project id
NEXT_PUBLIC_XANALYST_GA_ID=G-... # GA4 measurement idWhen set, AnalyticsTracker loads the script (no other code needed), every
track() is also sent as a Clarity event and a GA4 event (GA4 skips
page_viewed; it records page views itself), and identify() sets the
Clarity user and the GA4 user_id. Ids can also be passed as
<AnalyticsTracker clarityId="..." gaId="..." />. Unset ids load nothing.
Neither loads on local hosts (localhost, 127.0.0.1, LAN IPs, *.local), so
development traffic stays out of GA4 / Clarity. Events still go to /api/track.
Release
# bump "version" in package.json, then
npm publishprepublishOnly builds dist/ before publishing.
