@traffical/js-client is the framework-agnostic browser SDK. It fetches the config bundle, resolves parameters in the browser, tracks exposure events, and manages anonymous-user identity automatically.
If you’re using React or Svelte, prefer the React SDK or Svelte SDK — they wrap this client with framework-native APIs.
Installation
Setup
Use an SDK key (
traffical_sk_..., scopes sdk:read+sdk:write) in browser code. SDK keys can fetch the bundle and send events but cannot modify configuration — safe to ship in client bundles.Resolving parameters
getParams(context, defaults) and decide(context, defaults) take context first, defaults second, and both are synchronous. In the browser you can usually omit the unit key — the SDK fills it from the auto-generated stable ID (see below).
The legacy object-bag form —
getParams({ context, defaults }) / decide({ context, defaults }) — still works but is deprecated. Prefer the positional form.Anonymous users and identify
Browser-side experiments often need to resolve parameters before a user is logged in. To make that work without you having to think about it, the browser SDK auto-generates a stable UUID on first visit, stores it in localStorage (with a cookie fallback), and fills the project’s unit-key slot with that value whenever the caller doesn’t pass one.
So if the project’s unit key is userId, the SDK effectively passes context.userId = "<auto-generated-uuid>" on every pre-login resolution. Bucketing works, exposures fire, the user gets a stable assignment for as long as their localStorage persists.
When the user logs in, call identify(realUserId):
userId. Every subsequent resolution uses the new value.
You can read the current stable ID with traffical.getStableId(). After identify(), this returns the value you passed in.
Tracking events
track(event, properties?, options?) — the event name first, an optional properties payload second, and an options bag (unitKey, decisionId, value, values, eventTimestamp) third. In the browser unitKey defaults to the current stable ID, so you rarely pass it:
Lifecycle
createTrafficalClient (async) awaits the first bundle fetch. When you can’t await at construction, use createTrafficalClientSync and await readiness before the first resolution:
close(). It stops the background refresh and awaits a final event flush before returning (on page unload it falls back to sendBeacon so the last batch still ships):
destroy() still exists but is deprecated — prefer close(), which awaits the final flush.Options
Deprecated option aliases. Earlier releases used
eventBatchSize, eventFlushIntervalMs, and a single requestTimeoutMs. These still work and forward to the canonical names above (batchSize, flushIntervalMs, and the three-way timeout split). requestTimeoutMs is honored as the legacy fallback for any of configTimeoutMs / eventsTimeoutMs / resolveTimeoutMs that you don’t set explicitly. Prefer the canonical names — the aliases will be removed in a future major.Bundle caching
The browser SDK caches the bundle in memory. The first page load fetches it; subsequent loads benefit from HTTP caching at the CDN edge (Cache-Control: public, max-age=60, must-revalidate with ETag). On warm loads, resolution is available immediately.
Plugins
The browser SDK has a small plugin system that powers some of Traffical’s other tools:Framework SDKs
If you’re using React or Svelte, use the framework-specific SDKs instead:@traffical/react— provider anduseTrafficalhook@traffical/svelte— stores and context API
@traffical/js-client and expose the same plugin system.