Back to Blog
Developer Guide

How to Build a Simple A/B Test with JavaScript (Code Included)

February 15, 2025
15 min read

Want to understand how A/B testing works under the hood? Let's build a simple client-side A/B test from scratch. By the end, you'll understand exactly what tools like ExperimentHQ do — and when you should use a tool vs building your own.

Prerequisites

  • Basic JavaScript knowledge
  • Understanding of the DOM
  • A website to test on

The Core Concept

Every A/B test has three parts:

1

Assignment

Randomly assign visitors to a variant

2

Modification

Apply the variant's changes to the page

3

Tracking

Record which variant converted

Step 1: Assign Visitors to Variants

First, we need to randomly assign each visitor to a variant — and remember that assignment so they see the same variant on return visits.

variant-assignment.js
function getVariant(experimentId, variants) {
  // Check if user already has an assignment
  const storageKey = `exp_${experimentId}`;
  let variant = localStorage.getItem(storageKey);
  
  if (!variant) {
    // Randomly assign to a variant
    const randomIndex = Math.floor(Math.random() * variants.length);
    variant = variants[randomIndex];
    localStorage.setItem(storageKey, variant);
  }
  
  return variant;
}

// Usage
const variant = getVariant('homepage-headline', ['control', 'variant-a']);
console.log(`User assigned to: ${variant}`);

Note on localStorage

localStorage works for basic tests, but it's cleared when users clear their browser data. Production tools use more robust methods.

Step 2: Apply the Variant Changes

Now we need to modify the DOM based on which variant the user is in. Here's a simple example:

apply-variant.js
function applyVariant(variant) {
  const headline = document.querySelector('h1');
  
  if (variant === 'variant-a') {
    headline.textContent = 'Save 10 Hours Every Week';
  }
  // 'control' keeps the original headline
}

// Run when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
  const variant = getVariant('homepage-headline', ['control', 'variant-a']);
  applyVariant(variant);
});

The Flicker Problem

If you wait for DOMContentLoaded, users might see the original content flash before the variant loads. This is called "flicker." Production tools solve this by hiding content until the variant is applied, or by running synchronously in the <head>.

Step 3: Track Conversions

Finally, we need to track when users convert (e.g., click a button, submit a form):

track-conversion.js
function trackConversion(experimentId, variant) {
  // Send to your analytics backend
  fetch('/api/track', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      experimentId,
      variant,
      timestamp: Date.now()
    })
  });
}

// Track when CTA is clicked
document.querySelector('.cta-button').addEventListener('click', () => {
  const variant = localStorage.getItem('exp_homepage-headline');
  trackConversion('homepage-headline', variant);
});

Complete Working Example

Here's everything together in a single script:

ab-test.js
(function() {
  const EXPERIMENT_ID = 'homepage-headline-v1';
  const VARIANTS = ['control', 'variant-a'];
  
  // 1. Get or assign variant
  const storageKey = `exp_${EXPERIMENT_ID}`;
  let variant = localStorage.getItem(storageKey);
  
  if (!variant) {
    variant = VARIANTS[Math.floor(Math.random() * VARIANTS.length)];
    localStorage.setItem(storageKey, variant);
  }
  
  // 2. Apply changes
  document.addEventListener('DOMContentLoaded', () => {
    if (variant === 'variant-a') {
      const headline = document.querySelector('h1');
      if (headline) {
        headline.textContent = 'Save 10 Hours Every Week';
      }
    }
    
    // 3. Track conversions
    const cta = document.querySelector('.cta-button');
    if (cta) {
      cta.addEventListener('click', () => {
        console.log(`Conversion: ${EXPERIMENT_ID}, ${variant}`);
        // Send to your analytics here
      });
    }
  });
})();

When to Build vs Use a Tool

Build Your Own If:

  • You want to learn how A/B testing works
  • You have very simple, one-off tests
  • You have engineering time to maintain it

Use a Tool If:

  • You need a visual editor (no-code)
  • You want proper statistical analysis
  • You need flicker-free implementation
  • You want to run multiple tests easily

Wrapping Up

Building a basic A/B test is straightforward. But production-grade testing requires handling edge cases: flicker prevention, statistical significance, cross-device tracking, and more. That's why most teams use dedicated tools.

Now you understand what's happening under the hood — which makes you a better user of any A/B testing tool.

Share this article

Ready to start A/B testing?

Free forever plan available. No credit card required.