Skip to main content
Create project
Features

Experiments

Run A/B tests and multivariate experiments. Assign users to variants, track exposure, and measure results.

Running an Experiment

Hook approach

The useExperiment hook returns the assigned variant string and an optional payload object for the current user. Use variant to switch between UI branches and payload to pass variant-specific configuration without hardcoding it.

CheckoutPage.tsx
import { useExperiment } from "@sheepit-ai/react";

function CheckoutPage() {
  const { variant, payload } = useExperiment("checkout_redesign");

  switch (variant) {
    case "streamlined":
      return <StreamlinedCheckout config={payload} />;
    case "one_page":
      return <OnePageCheckout config={payload} />;
    default:
      return <StandardCheckout />;
  }
}

Declarative approach with variant map

The Experiment component accepts a variants map keyed by variant name and renders the matching element automatically. Use fallback to handle the case where the experiment is not running or the user is not in the experiment.

HeroSection.tsx
import { Experiment } from "@sheepit-ai/react";

function HeroSection() {
  return (
    <Experiment
      experimentKey="hero_test"
      variants={{
        control: <HeroOriginal />,
        bold: <HeroBold />,
        minimal: <HeroMinimal />,
      }}
      fallback={<HeroOriginal />}
    />
  );
}

Render prop pattern

When you need both variant and payload inside JSX without a separate component, use the render prop form of Experiment.

Example
<Experiment experimentKey="pricing_test">
  {({ variant, payload }) => (
    <PricingTable
      layout={variant}
      discount={payload?.discount}
    />
  )}
</Experiment>

Server-side

Use getGoaTech() from @sheepit-ai/server/nextjs to evaluate experiments in React Server Components or API route handlers. Pass the user ID so the assignment is consistent with client-side evaluation for the same user.

page.tsx (RSC)
const gt = getGoaTech();
const { variant } = await gt.experiment("onboarding_flow", userId);

How Assignment Works

  1. Traffic gating — only a configured percentage of users enter the experiment; the rest see the control experience
  2. Variant assignment — users inside the experiment are deterministically assigned to a variant using consistent hashing (murmur3); the same user always receives the same variant
  3. Persistence — client-side assignments are stored in localStorage so the user sees the same variant across sessions
  4. Exposure tracking — a $experiment_exposure event is automatically recorded once per session per experiment

Experiment Lifecycle

Experiments have four statuses. Only experiments in the Running state assign users to variants.

  • Draft — not yet running; no users are assigned
  • Running — actively assigning users to variants
  • Paused — stops new assignments; existing assignments persist so users continue to see the variant they were assigned
  • Completed — experiment finished; no new assignments

When an experiment is not in the Running state, useExperiment() returns { variant: "control" }.

Variants

Each experiment has one or more variants. Every experiment must include a control variant as the baseline. Each variant has the following fields:

  • key — unique identifier used in code (e.g. "control", "variant_b")
  • name — human-readable name shown in the dashboard
  • weight — traffic allocation weight (e.g. 50 for 50%)
  • is_control — marks the control/baseline variant
  • payload — optional JSON data attached to the variant, accessible via the payload field returned by useExperiment()

Measuring Results

Experiments work hand-in-hand with event tracking. Sheepit automatically joins $experiment_exposure events with any subsequent conversion events you track, so you can see the impact of each variant in the Experiments dashboard without writing any queries.

The typical flow looks like this:

  1. Run the experiment — users are assigned to variants
  2. Track conversion events as users interact with the variant UI
  3. View results in the Experiments dashboard — Sheepit joins exposure and conversion events automatically
PricingPage.tsx
function PricingPage() {
  const { variant } = useExperiment("pricing_test");
  const track = useTrack();

  return (
    <div>
      {variant === "annual_first" ? <AnnualPricing /> : <MonthlyPricing />}
      <button onClick={() => track("plan_selected", { variant })}>
        Choose Plan
      </button>
    </div>
  );
}

Best Practices

  • Run one experiment per page or component to avoid interaction effects between concurrent experiments
  • Let experiments run until they reach statistical significance before drawing conclusions
  • Always include a control variant as your baseline — it is the version you are comparing against
  • Use meaningful variant keys that describe the change (e.g. "annual_first" rather than "variant_a")
  • Track specific conversion events tied to your experiment goal, not just generic page views