@traffical/js-client
v0.10.1
Published
Traffical JavaScript SDK for browser environments
Downloads
1,111
Maintainers
Readme
@traffical/js-client
JavaScript SDK for browser environments with error boundaries, exposure deduplication, and smart event batching.
Installation
NPM
npm install @traffical/js-client
# or
bun add @traffical/js-clientCDN
<script src="https://cdn.traffical.io/js-client/v1/traffical.min.js"></script>Usage
NPM / ES Modules
import { createTrafficalClient } from '@traffical/js-client';
const traffical = await createTrafficalClient({
orgId: 'org_xxx',
projectId: 'proj_xxx',
env: 'production',
apiKey: 'pk_xxx',
});
// Get parameters
const params = traffical.getParams({
context: { userId: 'user_123' },
defaults: {
'ui.hero.title': 'Welcome',
'ui.hero.color': '#000',
},
});
// Make decision with tracking metadata
const decision = traffical.decide({
context: { userId: 'user_123' },
defaults: { 'ui.hero.title': 'Welcome' },
});
// Track exposure (automatically deduplicated)
traffical.trackExposure(decision);
// Track user events
traffical.track('purchase', { value: 99.99, orderId: 'ord_123' });
traffical.track('add_to_cart', { itemId: 'sku_456' });
// Track with explicit decision attribution
traffical.track('checkout_complete', { value: 1 }, { decisionId: decision.decisionId });CDN / Script Tag
<script src="https://cdn.traffical.io/js-client/v1/traffical.min.js"></script>
<script>
Traffical.init({
orgId: 'org_xxx',
projectId: 'proj_xxx',
env: 'production',
apiKey: 'pk_xxx',
}).then(function(traffical) {
var params = traffical.getParams({
context: { userId: 'user_123' },
defaults: { 'ui.hero.title': 'Welcome' },
});
console.log(params);
});
</script>Google Tag Manager
<script>
(function() {
var s = document.createElement('script');
s.src = 'https://cdn.traffical.io/js-client/v1/traffical.min.js';
s.onload = function() {
Traffical.init({
orgId: '{{Traffical Org ID}}',
projectId: '{{Traffical Project ID}}',
env: '{{Traffical Environment}}',
apiKey: '{{Traffical API Key}}',
});
};
document.head.appendChild(s);
})();
</script>Configuration Options
createTrafficalClient({
// Required
orgId: string,
projectId: string,
env: string,
apiKey: string,
// Optional
baseUrl?: string, // Default: https://sdk.traffical.io
refreshIntervalMs?: number, // Config refresh interval (default: 60000)
localConfig?: ConfigBundle, // Offline fallback config
eventBatchSize?: number, // Events per batch (default: 10)
eventFlushIntervalMs?: number, // Flush interval (default: 30000)
exposureSessionTtlMs?: number, // Dedup session TTL (default: 1800000)
plugins?: TrafficalPlugin[], // Plugins to register
});Changing User Identity
Use identify() to switch the user identity mid-session (e.g., after login). All framework providers (React, Svelte, React Native) automatically re-evaluate decisions and update the UI.
// After user logs in
traffical.identify('user_logged_in_123');
// After logout — revert to anonymous
traffical.identify(crypto.randomUUID());Unlike setStableId() (which silently changes the internal ID), identify() notifies all subscribers — framework providers re-render, the debug plugin updates, and DevTools reflects the change.
Parameter Overrides (Plugin API)
The client supports runtime parameter overrides intended for debugging and DevTools integration. These methods are available on the client instance but are designed for plugin use (not general application code):
// Apply overrides — only affects keys present in decide()/getParams() defaults
client.applyOverrides({ 'feature.enabled': true, 'feature.color': 'red' });
// Get current overrides
client.getOverrides(); // { 'feature.enabled': true, 'feature.color': 'red' }
// Clear all overrides
client.clearOverrides();
// Listen for override changes (used by framework providers for reactivity)
const unsub = client.onOverridesChange((overrides) => {
console.log('Overrides changed:', overrides);
});
unsub(); // unsubscribeOverrides are applied post-resolution in decide() and getParams(). They only affect keys that exist in the defaults object passed to those methods. Framework providers (React, Svelte, React Native) automatically re-evaluate when overrides change.
The Traffical DevTools debug plugin uses these methods to let developers force parameter values during development and QA.
Features
- Error Boundary - SDK errors never crash your app
- Exposure Deduplication - Same user/variant = 1 exposure per session
- Smart Batching - Events batched and flushed efficiently
- Beacon on Unload - Events sent reliably on page close
- Auto Stable ID - Anonymous user identification via localStorage/cookie
- Plugin System - Extensible via plugins
- DOM Binding Plugin - Auto-apply parameters to DOM elements
Plugins
DOM Binding Plugin
Automatically applies parameter values to DOM elements based on bindings configured via the Traffical Visual Editor.
import { createTrafficalClient, createDOMBindingPlugin } from '@traffical/js-client';
const traffical = await createTrafficalClient({
orgId: 'org_xxx',
projectId: 'proj_xxx',
env: 'production',
apiKey: 'pk_xxx',
plugins: [
createDOMBindingPlugin({
observeMutations: true, // Watch for DOM changes (SPA support)
debounceMs: 100, // Debounce reapplication
}),
],
});
// Parameters are automatically applied to DOM elements
// when getParams() or decide() is called
const params = traffical.getParams({
context: { userId: 'user_123' },
defaults: { 'hero.headline': 'Welcome' },
});
// Access the plugin for manual control
const bindingPlugin = traffical.getPlugin('dom-binding');
bindingPlugin?.applyBindings(); // Re-apply bindings
bindingPlugin?.getBindings(); // Get current bindingsThe plugin:
- Receives bindings from the config bundle via
onConfigUpdate - Applies parameter values to DOM elements via
onResolveandonDecision - Supports URL pattern matching (regex) for page-specific bindings
- Uses MutationObserver for SPA support
- Supports multiple property types:
innerHTML,textContent,src,href,style.*
Redirect Plugin
Run URL split tests (redirect experiments) where visitors are redirected to different landing page variants. The redirect plugin automatically triggers a decision on init, performs the redirect, and sets an attribution cookie. The attribution plugin ensures conversions on the variant page are attributed back to the experiment.
import {
createTrafficalClient,
createRedirectPlugin,
createRedirectAttributionPlugin,
} from '@traffical/js-client';
const traffical = await createTrafficalClient({
orgId: 'org_xxx',
projectId: 'proj_xxx',
env: 'production',
apiKey: 'pk_xxx',
plugins: [
createRedirectPlugin(),
createRedirectAttributionPlugin(),
],
});
// That's it — the redirect plugin calls decide() automatically on init.
// On entry pages, it redirects. On other pages, it's a no-op.
// Track goals as usual — the attribution plugin injects
// redirect experiment metadata into every track() call.
traffical.track('add_to_cart', { value: 29.99 });GTM Integration
On all pages, add the Traffical SDK with both redirect plugins:
<script src="https://cdn.traffical.io/js-client/v1/traffical.min.js"></script>
<script>
Traffical.init({
orgId: '{{Traffical Org ID}}',
projectId: '{{Traffical Project ID}}',
env: '{{Traffical Environment}}',
apiKey: '{{Traffical API Key}}',
plugins: [
Traffical.createRedirectPlugin(),
Traffical.createRedirectAttributionPlugin(),
],
});
</script>Track goal events from a separate GTM tag (e.g., triggered on "Add to Cart" click):
<script>
var client = Traffical.instance();
if (client) {
client.track('add_to_cart', { value: 29.99 });
}
</script>How It Works
Init — The redirect plugin's
onInitializehook receives the client and callsdecide()automatically.Entry page —
onBeforeDecisioninjectsurl.pathnameinto the context. The policy condition (e.g.,url.pathname startsWith /products/pillow) matches,redirect.urlresolves to the variant URL.onDecisionwrites an attribution cookie (traffical_rdr) and callswindow.location.replace().Variant page — The SDK loads again,
decide()runs, but the policy condition doesn't match the new URL, soredirect.urlstays empty and no redirect happens. The redirect-attribution plugin reads thetraffical_rdrcookie and injects the experiment metadata into everytrack()call.
Configuration
createRedirectPlugin({
parameterKey?: string, // Default: "redirect.url"
compareMode?: string, // "pathname" (default) or "href"
cookieName?: string, // Default: "traffical_rdr"
});
createRedirectAttributionPlugin({
cookieName?: string, // Default: "traffical_rdr"
expiryMs?: number, // Default: 86400000 (24 hours)
});Context Fields
The redirect plugin automatically adds these context fields:
| Field | Value | Example |
|-------|-------|---------|
| url.pathname | window.location.pathname | /products/pillow |
Use url.pathname in policy conditions to target specific pages.
Development
Build
# Install dependencies (from sdk/ root)
cd ../
bun install
# Build ESM + IIFE
cd js-client
bun run build
# Type check only
bun run typecheckOutput
| File | Format | Use Case |
|------|--------|----------|
| dist/index.js | ESM | npm / bundlers |
| dist/traffical.min.js | IIFE | CDN / script tag |
Release to CDN
# Requires wrangler CLI with R2 access configured
./scripts/release-cdn.shThis uploads to:
cdn.traffical.io/js-client/v{VERSION}/- Immutable, 1 year cachecdn.traffical.io/js-client/v{MAJOR}/- Latest major, 1 hour cachecdn.traffical.io/js-client/latest/- Latest, 5 minute cache
