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.