Ultra-lightweight A/B testing SDK. Zero dependencies. ~3KB gzipped.
Works with any website or JavaScript framework — vanilla JS, React, Vue, Angular, Next.js, or any SPA. Integrates with both the dashboard and the Visual Editor Chrome extension.
The fastest way to get started. Add to every page you want to test:
<!-- Anti-flicker cloak (optional but recommended for visual tests) -->
<style>.sw-cloak { opacity: 0 !important; }</style>
<body class="sw-cloak">
<!-- SplitWisp SDK -->
<script src="https://cdn.splitwisp.com/sdk/v1/tracker.umd.js"></script>
<script>
SplitWisp.init({
apiKey: 'pk_live_abc123',
endpoint: 'https://api.splitwisp.com',
}).then(function (t) {
// Visual changes are applied automatically.
// For code-based experiments, read the variant:
var variant = t.getVariant('exp_hero_headline');
if (variant && variant.variantName === 'short') {
document.querySelector('h1').textContent = 'Ship faster.';
}
});
</script>
The SplitWisp.init() convenience factory creates a Tracker instance, calls init(), and returns the tracker via a Promise. This is the recommended approach for <script> tag and Google Tag Manager usage.
npm install @splitwisp/tracker
import { Tracker } from '@splitwisp/tracker';
const tracker = new Tracker({
apiKey: 'pk_live_abc123',
endpoint: 'https://api.splitwisp.com',
});
await tracker.init();
// Check variant assignment
const variant = tracker.getVariant('exp_pricing_cta');
if (variant?.variantName === 'annual_first') {
showAnnualPricingFirst();
}
// Track a conversion
tracker.trackConversion('exp_pricing_cta', 9900);
Pass these options to the Tracker constructor or SplitWisp.init():
tracker.init(): Promise<Assignment[]>Fetches variant assignments for all active experiments for this session. Call once on page load. Results are cached in-memory for the page lifetime.
When applyVisualChanges is enabled (the default), visual changes from all assigned variants are automatically applied to the DOM using a MutationObserver that handles dynamically rendered elements.
After assignments are fetched, conversion goals configured on experiments are automatically set up — no additional code required.
Any events queued offline from previous page loads are drained and retried.
Returns: An array of Assignment objects.
tracker.getVariant(experimentId): Assignment | nullReturns the assigned variant for a specific experiment. Returns null if the experiment was not found or the session was not assigned.
const variant = tracker.getVariant('exp_hero_headline');
if (variant) {
console.log(variant.variantId); // e.g. "variant_a"
console.log(variant.variantName); // e.g. "Green Button"
console.log(variant.changes); // visual changes array (if any)
}
tracker.getAssignments(): Assignment[]Returns all current variant assignments as an array.
tracker.track(opts): Promise<void>Tracks an event (impression, conversion, click, etc.). Failed calls are queued to localStorage and retried on the next init().
UTM parameters (utm_source, utm_medium, utm_campaign) are automatically captured from the URL and attached to all track calls for source attribution.
tracker.trackConversion(experimentId, value?, properties?): Promise<void>Convenience wrapper that sends a "conversion" event for a specific experiment.
// Revenue conversion (value in cents)
tracker.trackConversion('exp_pricing_cta', 9900); // $99.00
// Simple binary conversion
tracker.trackConversion('exp_signup_flow');
// Conversion with metadata
tracker.trackConversion('exp_checkout', 14999, {
plan: 'pro',
coupon: 'SAVE20',
});
tracker.trackBeacon(opts): voidFire-and-forget tracking via the browser's sendBeacon API. Does not retry on failure. Use for page unload or visibility change events:
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
tracker.trackBeacon({ event: 'pagehide' });
}
});
tracker.autoTrackPageViews(): () => voidAutomatically tracks "pageview" events on the current page and on SPA navigations (via pushState, replaceState, and popstate). Also tracks "pagehide" via sendBeacon when the tab is hidden.
Returns: A teardown function to stop tracking.
const stopTracking = tracker.autoTrackPageViews();
// Later, to clean up:
stopTracking();
tracker.identify(userId): voidAssociate a known user ID with the current session. Call before or after init(). Enables cross-device attribution when a visitor logs in.
// After user signs in
tracker.identify('user_12345');
tracker.sessionId: stringRead-only property returning the current session ID.
Each assignment returned by init() or getVariant() has this shape:
interface Assignment {
experimentId: string; // e.g. "exp_hero_headline"
variantId: string; // e.g. "variant_a"
variantName: string; // e.g. "Green Button"
changes?: VisualChange[]; // visual changes to apply (if any)
conversionGoals?: ConversionGoal[]; // auto-tracking goals (if configured)
}
Visual changes are applied automatically unless applyVisualChanges: false is set. Conversion goals are tracked automatically with per-session deduplication. See Conversion Goals for details on the five supported goal types.
When using visual A/B tests created with the Visual Editor or the dashboard, add this CSS to prevent a flash of original content:
<style>
.sw-cloak { opacity: 0 !important; }
</style>
<body class="sw-cloak">
The SDK automatically removes the sw-cloak class after visual changes are applied — or after a safety timeout (default 5 seconds) to guarantee the page always becomes visible, even if init() fails.
The SDK is designed to fail gracefully:
track() calls are saved to localStorage (capped at 100 events) and retried on the next init() calltimeout (default 5s)429 (quota exceeded), the SDK caches the blocked state in sessionStorage for 1 hour and suppresses further requestslocalStorage is unavailableBrowser → tracker.umd.js (~3KB gzip)
├─ GET /v1/assign?apiKey=...&sessionId=... → variant assignments + visual changes + goals
├─ POST /v1/track { event, sessionId, ... } → event ingestion
├─ localStorage: _sw_sid (session ID), _sw_q (offline queue)
└─ sessionStorage: _sw_utm (UTM params), _sw_goals (fired goals), _sw_blocked (rate limit)