Skip to main content
@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

npm install @traffical/js-client

Setup

import { createTrafficalClient } from "@traffical/js-client";

const traffical = await createTrafficalClient({
  orgId: "org_acme",
  projectId: "proj_marketplace",
  env: "production",
  apiKey: "traffical_sk_...",
});
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

const params = traffical.getParams({
  context: { userId: "user_789" },
  defaults: {
    "homepage.hero_headline": "Welcome back",
    "homepage.show_banner": true,
  },
});

document.querySelector("h1")!.textContent = params["homepage.hero_headline"];
An exposure event is emitted automatically the first time a user/assignment combination is resolved in a session.

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):
traffical.identify("user_789");
This overwrites the stored stable ID with the real userId. Every subsequent resolution uses the new value.
Bucketing changes at login. The pre-login bucket and the post-login bucket are computed from different inputs (hash(uuid + layerId) vs hash("user_789" + layerId)) — they almost always land in different allocations. A user who saw the treatment variant while anonymous may see the control variant after logging in, and vice versa.This is unavoidable with a single unit of randomization per project. There’s no “anonymous-to-identified” continuity layer: the SDK doesn’t remember which bucket the stable ID was in and remap the userId to match. If you need user-stable assignments to persist across login, do the experiment on logged-in users only — gate the policy with a condition that the unit key matches a real user-ID format, or have your backend force identify() before the SDK ever resolves the parameter.
You can read the current stable ID with traffical.getStableId(). After identify(), this returns the value you passed in.

Tracking events

traffical.track("signup", { unitKey: "user_789" });

traffical.track("purchase", {
  unitKey: "user_789",
  properties: { order_total: 29.99, currency: "USD" },
});
Events are batched in memory and flushed in the background.

Options

OptionTypeDefaultDescription
orgIdstringrequiredOrganization ID
projectIdstringrequiredProject ID
envstringrequiredEnvironment name
apiKeystringrequiredSDK key (traffical_sk_..., scopes sdk:read+sdk:write) — browser-safe
baseUrlstringhttps://sdk.traffical.ioSDK API base URL
refreshIntervalMsnumber60000Bundle refresh interval
localConfigConfigBundleEmbedded bundle for cold-start
evaluationMode"bundle" | "server""bundle"See how it works
attributionMode"cumulative" | "decision""cumulative"Track-event attribution strategy
trackDecisionsbooleantrueEmit decision events alongside exposures
disableAutoStableIdbooleanfalseDisable auto-generation of stableId
storageStorageProviderLocalStorageProviderPersistence for the stable ID
pluginsTrafficalPlugin[][]Plugins (DOM bindings, redirect tests, devtools)

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:
PluginPurpose
DOM bindingsAuto-apply parameter values to DOM elements (used by the visual editor)
Redirect testsURL split-testing — redirect users to different URLs based on assignment
Warehouse-native loggerForward assignment events to your warehouse
DevToolsLive SDK inspection in the browser (used by the DevTools bookmarklet)
import { createTrafficalClient, createDOMBindingPlugin } from "@traffical/js-client";

const traffical = await createTrafficalClient({
  orgId, projectId, env, apiKey,
  plugins: [createDOMBindingPlugin()],
});

Framework SDKs

If you’re using React or Svelte, use the framework-specific SDKs instead: Both wrap @traffical/js-client and expose the same plugin system.