Web (multi-package)
Install the web SDK
Per-package install commands for npm, pnpm, yarn, bun, and CDN. Plus framework-specific notes for Next.js, Vite, Remix, and Astro.
Every web SDK package can be installed independently. The only required one is @sankofa/browser; everything else (Catch, Switch, Config, Pulse, Replay, React hooks) layers on top.
Install the core
npm install @sankofa/browserpnpm add @sankofa/browseryarn add @sankofa/browserbun add @sankofa/browserAdd product packages
Install whichever extras you need. None of them require special peer-dependency setup.
# Error tracking + breadcrumbs
npm install @sankofa/catch
# Feature flags + exposures
npm install @sankofa/switch
# Typed remote config
npm install @sankofa/config
# Behavior-triggered surveys
npm install @sankofa/pulse
# Session replay (rrweb)
npm install @sankofa/replay-rrweb
# React hooks (requires React 18 or 19)
npm install @sankofa/reactIf you install all seven at once: npm install @sankofa/browser @sankofa/catch @sankofa/switch @sankofa/config @sankofa/pulse @sankofa/replay-rrweb @sankofa/react. Total bundle (gzipped) for the worst case is ~50 KB.
CDN install
For a bundler-free site, drop a single <script> in your HTML:
<script
src="https://cdn.jsdelivr.net/npm/@sankofa/browser/dist/sankofa.min.js"
defer
></script>
<script>
window.addEventListener("load", () => {
Sankofa.init({
apiKey: "sk_live_...",
endpoint: "https://api.sankofa.dev",
});
});
</script>The CDN bundle exposes a Sankofa global. Plugins on the CDN load from sub-paths:
<script src="https://cdn.jsdelivr.net/npm/@sankofa/browser/dist/sankofa.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/@sankofa/switch/dist/switch.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/@sankofa/config/dist/config.min.js" defer></script>For production, pin to a specific version (e.g. @sankofa/browser@1.0.4) so a future major release doesn't surprise you.
Framework recipes
"use client";
import { Sankofa } from "@sankofa/browser";
import { catchPlugin } from "@sankofa/catch";
import { switchPlugin } from "@sankofa/switch";
let initialized = false;
export function ensureSankofa() {
if (initialized || typeof window === "undefined") return;
initialized = true;
Sankofa.init({
apiKey: process.env.NEXT_PUBLIC_SANKOFA_KEY!,
endpoint: "https://api.sankofa.dev",
plugins: [catchPlugin(), switchPlugin()],
});
}import { ensureSankofa } from "./sankofa.client";
export default function RootLayout({ children }: { children: React.ReactNode }) {
if (typeof window !== "undefined") ensureSankofa();
return <html lang="en"><body>{children}</body></html>;
}import { Sankofa } from "@sankofa/browser";
import { switchPlugin } from "@sankofa/switch";
Sankofa.init({
apiKey: import.meta.env.VITE_SANKOFA_KEY,
endpoint: "https://api.sankofa.dev",
plugins: [switchPlugin()],
});import { Sankofa } from "@sankofa/browser";
Sankofa.init({
apiKey: window.ENV.SANKOFA_KEY,
endpoint: "https://api.sankofa.dev",
});Pass the API key from server context via Remix's loader → meta pattern; never inline a live key in the bundle.
---
const SANKOFA_KEY = import.meta.env.PUBLIC_SANKOFA_KEY;
---
<script>
import { Sankofa } from "@sankofa/browser";
Sankofa.init({
apiKey: import.meta.env.PUBLIC_SANKOFA_KEY,
endpoint: "https://api.sankofa.dev",
});
</script>import { Sankofa } from "@sankofa/browser";
import { PUBLIC_SANKOFA_KEY } from "$env/static/public";
Sankofa.init({
apiKey: PUBLIC_SANKOFA_KEY,
endpoint: "https://api.sankofa.dev",
});TypeScript
Every package ships TypeScript types — no @types/... package needed. The types are strict (noImplicitAny, strictNullChecks) and documented inline.
import { Sankofa, type SankofaInitConfig } from "@sankofa/browser";
const config: SankofaInitConfig = {
apiKey: "sk_live_...",
endpoint: "https://api.sankofa.dev",
flushIntervalMs: 5000,
};
Sankofa.init(config);