For the complete documentation index, see llms.txt. This page is also available as Markdown.

Framework recipes

Drop-in setup for React, Next.js, Vue, Svelte, and other JS frameworks.

The browser SDK (@millimetric/track) is framework-agnostic — it's just init, track, identify, page, flush. This page collects the small, copy-paste-ready integrations for the frameworks people actually use.

If your framework isn't listed, the pure-JS browser snippet Just Works on any HTML page.

Sub-recipes

Common pattern across all of them

Three things, in order:

  1. Init once — at the top of your app, with your pk_* key.

  2. Identify on auth — when you know who the visitor is.

  3. Track on intent — buttons, conversions, the things you'd put in a funnel.

Auto-pageviews (including SPA navigations) are on by default; you usually don't have to call page() yourself.

Where to put the key

Framework
Env var name (suggested)
Read at

Vite (React/Vue/Svelte)

VITE_AOA_KEY

import.meta.env.VITE_AOA_KEY

Next.js

NEXT_PUBLIC_AOA_KEY

process.env.NEXT_PUBLIC_AOA_KEY

Nuxt

NUXT_PUBLIC_AOA_KEY

useRuntimeConfig().public.aoaKey

SvelteKit

PUBLIC_AOA_KEY

import { PUBLIC_AOA_KEY } from "$env/static/public"

The pk_* key is browser-safe — it's origin-allowlisted on the server. Never expose sk_* or rk_* to the browser.

Anti-patterns

  • Calling init() in a render function. Init is idempotent but inits-per-render is wasteful. Wrap in useEffect (React) / onMount (Svelte) / mounted() (Vue).

  • Tracking inside useEffect cleanup. The cleanup runs on unmount and on every re-render — easy to over-track. Track on the user action (onClick), not on the effect lifecycle.

  • Calling flush() synchronously in render. It returns a promise; awaiting it in render breaks SSR.

  • Setting user_id from your URL. Use identify(user.id) once after auth resolves; don't pass user_id per-call.

Last updated

Was this helpful?