✨ Open Source · MIT License

A/B testing made
ridiculously simple

TinySplits is a tiny, modular A/B experiment runner. Define experiments as self-contained modules with their own HTML, CSS, and tracking. Your client loads one script — zero config on their end.

your-website.html
<!-- That's it. One line. -->
<script src="https://cdn.example.com/tinysplits.js"></script>
~4.5 kB
Bundle size
~2 kB
Gzipped
0
Dependencies
0
Client config

Everything you need.
Nothing you don't.

Each experiment is a single file with its own HTML, CSS, and tracking logic — completely self-contained and independently toggleable.

📦
Self-Contained Modules
Each experiment bundles its own HTML template, CSS styles, and event tracking in a single file. No scattered code across your codebase.
🔀
PostHog-Powered
Automatically resolves variants via PostHog feature flags. Falls back gracefully if PostHog isn't available.
🎯
Smart Preconditions
Validates URL patterns and target elements before calling PostHog. No wasted network calls for experiments that won't run.
🌍
Environment-Aware
Auto-detects dev, staging, or prod from the client's hostname. Roll out experiments gradually — test on dev first, then staging, then prod.
🔧
Toggle On/Off
Enable or disable any experiment with a boolean flag. The code stays in your codebase — just flip, rebuild, deploy.
🪶
Tiny Footprint
Under 5 kB total. Zero runtime dependencies. Loads fast, executes fast, doesn't impact page performance.
🧑‍💻
QA-Friendly
Force any variant with ?preview=test. No PostHog setup needed during development.

How the runner works

TinySplits runs through a validation pipeline before executing each experiment. No wasted PostHog calls, no unnecessary DOM lookups.

📋
Registered?
enabled: true
🌍
Environment?
dev / staging / prod
🌐
URL Match?
pathname.includes()
🎯
Target Exists?
querySelector()
🔀
Resolve Variant
PostHog flag
🚀
Execute
run({ variant })

Up and running in 3 steps

Create an experiment, register it, build. That's the entire workflow.

1
Create an experiment
Add a new file in src/experiments/. Define your PostHog key, target, styles, HTML, and run logic — all in one file.
export default defineExperiment({ key: 'hero-banner-test', enabled: true, target: '.hero', styles, run({ variant, injectHTML }) { ... } });
2
Register it
Import your experiment in the registry file. This is the single source of truth for which experiments are in the bundle.
import heroBanner from './hero-banner'; const experiments = [ heroBanner, // ← add here ];
3
Build & deploy
Run the build command, upload the output to your CDN. Done. No changes needed on the client websites.
$ pnpm build ✓ 7 modules transformed dist/tinysplits.js 4.5 kB │ gzip: 2.0 kB ✓ built in 88ms

A complete experiment

Here's a real experiment that injects a promo banner with its own styles, HTML template, and click tracking — all in a single file.

src/experiments/promo-banner.ts
import { defineExperiment } from '../core/types';

const styles = `
  .ts-promo {
    background: linear-gradient(135deg, #6366f1, #8b5cf6);
    color: white; padding: 14px 20px; border-radius: 8px;
    display: flex; justify-content: space-between;
  }
`;

const html = `
  <div class="ts-promo">
    <span>🚀 Get 20% off today!</span>
    <button class="ts-promo__cta">Claim Now</button>
  </div>
`;

export default defineExperiment({
  key: 'promo-banner-test',
  enabled: true,
  target: '.main-content',
  styles,
  urlMatch: '/pricing',
  run({ variant, injectHTML, trackEvent }) {
    if (variant === 'test') {
      const el = injectHTML('.main-content', html, 'beforebegin');
      el?.querySelector('.ts-promo__cta')?.addEventListener('click', () => {
        trackEvent('promo_clicked', { page: 'pricing' });
      });
    }
  },
});

See it in action

The experiment runner is loaded on this page. Click the button below to activate the test variant and see an experiment inject a component in real-time.

⚪ Variant: control
Experiment: example-banner-test
The experiment targets .demo-content below. When the test variant is active, a promo banner will be injected above it.
📄 This is the .demo-content target element. The experiment will inject its banner above this area.
[TinySplits] Waiting for demo interaction...

Ready to split test?

Get started with TinySplits in minutes. It's open source, free, and ridiculously small.