React
Two equivalent setups: a plain init module (what npx sonder-init
writes for Vite + React), or a provider if you want the client available as a hook.
Init module (recommended)
npm install @sonderhq/sdk
// src/sonder.ts — created for you by `npx sonder-init`
import { initSonder } from "@sonderhq/sdk";
export const sonder = initSonder({
writeKey: "snd_pk_your_write_key",
buildId: import.meta.env.VITE_SONDER_BUILD_ID ?? import.meta.env.MODE
});
Then import it once at the very top of your entry file, before React renders:
// src/main.tsx
import "./sonder";
That's everything. Autocapture records clicks, inputs (values masked), submits, and route changes; rage and dead clicks are derived in the browser with a state probe of what the user saw.
Provider + hook
Prefer context? SonderProvider creates the client and
useSonder() hands it to any component:
import { SonderProvider, useSonder } from "@sonderhq/sdk/react";
import type { ReactNode } from "react";
export function AppShell({ children }: { readonly children: ReactNode }) {
return (
<SonderProvider writeKey="snd_pk_your_write_key">
{children}
</SonderProvider>
);
}
// Anywhere below the provider, the client is one hook away.
export function UpgradeButton() {
const sonder = useSonder();
return (
<button onClick={() => sonder.track("upgrade_clicked", { plan: "team" })}>
Upgrade
</button>
);
}
Custom events and identity
Optional, and never required for baseline value. They sharpen milestones and outcomes in your workspace config:
import { sonder } from "./sonder"; // the client created at init
// Custom events are optional sharpeners, never a prerequisite. Sonder maps
// them onto milestones and outcomes in your workspace config.
sonder.track("checkout_completed", { plan: "team", seats: 4 });
// Tie sessions to a stable person reference. Traits are masked like any
// other properties (an email value becomes [masked]).
sonder.identify("user-8f31", { plan: "trial", signup_week: "2026-W27" });
// Force a flush before a hard navigation if you need one; otherwise the SDK
// flushes every 5 seconds and on beforeunload.
await sonder.flush();
Release stamping
Pass your deploy identifier as buildId (the sample above uses
VITE_SONDER_BUILD_ID). When you ship a fix, Sonder compares the
confirm-metric across builds, resolves the issue when it holds, and reopens it if a
later build regresses.
Vue, Angular, Svelte
First-class providers are on the roadmap. Today, use the framework-agnostic plain JavaScript setup: the SDK has no React dependency at its core, and autocapture works identically.