— Browser SDK
Browser SDK (sdk-js)
The thinnest path to capturing events, errors, and sessions in any browser-based JavaScript app.
Install
pnpm add @emit-vision/sdk-js
Initialization
Call init() as early as possible — before the rest of your app starts emitting events.
import { init } from "@emit-vision/sdk-js";
init({
apiKey: "evk_your_ingest_key",
environment: "production",
release: "[email protected]",
autoCapture: {
errors: true,
unhandledRejections: true,
pageViews: true,
},
flushIntervalMs: 5000,
batchSize: 20,
});Key options:
apiKey— your ingest token (starts withevk_), safe to include in browser bundles.environment— labels telemetry so you can filter bydevelopment,staging, orproduction.release— tags events with the app version or git SHA.autoCapture.pageViews— fires a$page_viewon load and on every SPA navigation (pushState,replaceState,popstate).autoCapture.errors— captures unhandled exceptions and promise rejections, including the current page URL automatically.flushIntervalMs— automatic batch flush cadence in milliseconds.batchSize— queue size that triggers an immediate flush.
Capturing events
Use captureEvent(name, properties, options?) for product actions. Keep names in snake_case with a verb_noun pattern.
import { captureEvent } from "@emit-vision/sdk-js";
captureEvent(
"user_signed_up",
{ plan: "pro", source: "pricing_page" },
{
tags: { feature_flag: "new_checkout", ab_test_variant: "b" },
context: { route: "/signup" },
},
);Use tags for values you expect to filter on frequently, especially high-cardinality metadata like feature flags and experiment variants.
Identifying users
Call identify() after login or sign-up so later events include user context.
import { identify } from "@emit-vision/sdk-js";
identify("user_123", {
email: "[email protected]",
username: "myusername",
});Do not send passwords, session tokens, raw auth headers, or any other secrets. User context is attached to subsequent events until you change it again.
Capturing errors
Use captureError() when you are already inside a try/catch and want to attach context. Leave autoCapture enabled for truly unhandled failures.
import { captureError } from "@emit-vision/sdk-js";
try {
await saveSettings();
} catch (error) {
captureError(
error instanceof Error ? error : new Error("save settings failed"),
{ context: { route: "/settings", action: "save_settings" } },
);
}Flushing
The SDK batches automatically, but call flush() when timing matters:
import { captureEvent, flush } from "@emit-vision/sdk-js";
captureEvent("checkout_submitted", { plan: "pro" });
await flush();
window.location.assign("/thanks");Useful before SPA navigation, on logout, or after critical form submissions.
Tracking page views
Enable automatic tracking via autoCapture.pageViews in init(), or call capturePageView() manually if you need control over when it fires.
import { capturePageView } from "@emit-vision/sdk-js";
// Uses window.location.pathname automatically
capturePageView();
// Or pass an explicit path
capturePageView("/pricing");Each $page_view event is automatically associated with the anonymous visitor ID generated on first load, so page traffic counts in the dashboard reflect all visitors — not just identified users.
Common mistakes
- Calling
init()inside a component render path — this re-initializes the SDK repeatedly and resets queued telemetry. Call it once at app entry (e.g.,main.ts). - Sending PII in event properties— avoid tokens, passwords, and free-form user input you don't control.
- Navigating without flushing — call
await flush()before key navigation so the last action is not lost. - Passing a plain string to
captureError()— wrap it innew Error(message)to preserve a stack trace.