/Docs

Variation Code

Each variation in an experiment can have its own CSS and JavaScript. Understanding when and how this code runs — and how the initVariation wrapper gives you lifecycle control — helps you write variations that are reliable, fast, and easy to clean up.

CSS injection

When a visitor is assigned to a variation, the variation's CSS is injected first. A <style>tag containing all the variation's CSS rules is appended to the document's <head> element. This happens synchronously during the experiment evaluation phase, while the page is still hidden by the anti-flicker mechanism.

Because the CSS is in the <head>, it applies to the entire page immediately. Any elements that match your CSS selectors will be styled according to the variation before the visitor ever sees the page.

Example variation CSS
css
1/* This CSS is injected into <head> for visitors in this variation */
2.hero-title {
3 font-size: 3rem;
4 color: #1d4ed8;
5}
6
7.cta-button {
8 background-color: #22c55e;
9 border-radius: 9999px;
10 padding: 1rem 2rem;
11}
Tip
CSS injected by A vs B overrides your page's existing styles because it appears later in the document than your stylesheet links. If your existing styles use !important and you cannot override them, you may need to add !important to your variation CSS as well.

JavaScript injection — the initVariation wrapper

After CSS, the variation's JavaScript runs. Your code must be wrapped in a function named initVariation that receives a single options argument:

Required wrapper function signature
javascript
1function initVariation(options) {
2 // Your variation code here
3}

The snippet calls initVariation(options) automatically. The editor blocks saving if the wrapper function is missing or renamed.

When initVariation runs:

  • The variation's CSS is already applied (CSS is injected first).
  • The DOM is fully built (the page's HTML has been parsed).
  • The window.avsb global API is available.
  • The page is still hidden by the anti-flicker mechanism.

The options object

The options argument provides experiment context, lifecycle hooks, self-cleaning helpers, and tracking utilities — everything your variation needs without reaching for global state.

Context properties

NameDescription
options.experimentIdstringOptional

The unique identifier for the experiment.

options.experimentNamestringOptional

The display name of the experiment.

options.variationIdstringOptional

The unique identifier for the variation assigned to this visitor.

options.variationNamestringOptional

The display name of the variation (e.g. "Control", "Variant 1").

options.variationType'control' | 'variant'Optional

Whether this visitor was assigned the control or a variant.

Lifecycle hooks

NameDescription
options.onRemove(callback: () => void) => voidOptional

Register a callback to run when the variation is removed — on SPA navigation, experiment teardown, or when the visitor calls avsb.disable(). Use this to undo DOM changes that the self-cleaning helpers cannot handle automatically.

Self-cleaning helpers

These helpers work exactly like their native browser equivalents, but anything you start through them is automatically torn down when the variation is removed. You never need to cancel them manually.

NameDescription
options.waitUntil(conditionFn, callback, opts?) => handleOptional

Polls a condition function and calls the callback with the truthy result when the condition is met. Uses a MutationObserver + 100ms polling internally. Automatically cancelled on variation removal.

options.setTimeout(fn: () => void, ms?: number) => handleOptional

Like window.setTimeout, but the timer is automatically cleared when the variation is removed.

options.setInterval(fn: () => void, ms?: number) => handleOptional

Like window.setInterval, but the interval is automatically cleared when the variation is removed.

options.addEventListener(target, type, handler, opts?) => voidOptional

Like target.addEventListener, but the listener is automatically removed when the variation is removed.

Tracking utilities

NameDescription
options.track.event(shortId: number, properties?: { revenue?: number }) => voidOptional

Manually fire a custom metric event for this visitor. The shortId must match a metric configured in your project.

options.track.segment(segmentKey: string, segmentValue: string) => voidOptional

Send a segment value for this visitor.

options.getVisitorId() => stringOptional

Returns the current visitor's unique identifier.

options.getVariation(experimentId: string) => string | nullOptional

Returns the variation ID assigned to this visitor for a given experiment, or null if not assigned.

options.getActiveExperiments() => Array<{ experimentId: string; variationId: string }>Optional

Returns all experiments the current visitor is currently active in.

options.forceVariation(experimentId: string, variationId: string) => booleanOptional

Forces the current visitor into a specific variation of another experiment. Returns true if the variation was applied successfully.

Basic example

A simple variation that changes a heading and reverts it when the variation is removed (for example, on SPA navigation). Because initVariation runs only after the experiment has already activated, DOM mutations go directly in the function body — there is no need for an additional activation wrapper:

Change a heading and revert on removal
javascript
1function initVariation(options) {
2 // The function body IS the activation moment — apply changes directly
3 const heading = document.querySelector('.hero h1');
4 if (heading) {
5 heading.dataset.originalText = heading.textContent;
6 heading.textContent = 'The faster way to grow your business';
7 }
8
9 // Revert DOM changes when the variation is removed (e.g. on SPA navigation)
10 options.onRemove(() => {
11 const heading = document.querySelector('.hero h1');
12 if (heading && heading.dataset.originalText) {
13 heading.textContent = heading.dataset.originalText;
14 }
15 });
16}

Waiting for dynamic content

On single-page applications, some elements are rendered by the JavaScript framework after the initial DOM parse. Use options.waitUntil to delay your DOM work until the target element is ready. Because it runs through options, it is automatically cancelled if the variation is torn down while waiting.

Wait for a third-party widget before styling it
javascript
1function initVariation(options) {
2 options.waitUntil(
3 // Condition: both the global and the DOM node must exist
4 () => window.Intercom && document.querySelector('#intercom-container'),
5 (container) => {
6 // Safe to modify — the widget is ready
7 container.classList.add('variant-position');
8
9 // Revert on removal
10 options.onRemove(() => {
11 const el = document.querySelector('#intercom-container');
12 if (el) el.classList.remove('variant-position');
13 });
14 },
15 { timeout: 5000 } // Give up after 5 seconds
16 );
17}

Accessing the DOM

Because variation JavaScript runs after the DOM is fully parsed, you can use standard DOM APIs like document.querySelector, document.getElementById, and so on directly in the initVariation function body without waiting for a DOMContentLoaded event.

On single-page applications, elements rendered by the framework after the initial page load may not be present yet. Use triggers with options.waitUntil to delay activation until the element exists, or use options.waitUntil directly inside initVariation.

Error handling

Errors thrown inside initVariation are not caught by A vs B. If your variation JS throws an exception, it will propagate normally in the browser. Always write defensive code:

Defensive variation code
javascript
1function initVariation(options) {
2 // Good — check that the element exists before modifying it
3 const button = document.querySelector('.cta-button');
4 if (button) {
5 button.textContent = 'Start free trial';
6 }
7
8 // Bad — throws an error if .cta-button doesn't exist
9 // document.querySelector('.cta-button').textContent = 'Start free trial';
10}

Cleanup on SPA navigation

On single-page applications, when the visitor navigates away from a page where an experiment is running, A vs B automatically:

  • Removes the <style>tag containing the variation's CSS.
  • Cancels all timers, intervals, listeners, and waitUntil observers started via the options helpers.
  • Runs any callbacks registered with options.onRemove.

Note that automatic teardown does not undo DOM text or attribute changes your code made — only the resources started through the options helpers are auto-cleaned. If your variation mutated the DOM directly, register an options.onRemove callback to reverse those changes.

Control variation has no code
The Control variation is the original, unmodified experience. It never has CSS or JS injected. A visitor in the Control group sees your page exactly as it would appear without A vs B running at all.