TestaDocs
Docs/Integration/React

React integration

For React single-page apps with no server of their own — Vite, Create React App, Lovable, or anything deployed as static files. Grab your project ID from the dashboard (Get your script) and replace {YOUR_PROJECT_ID} below.

Using Next.js? Use the Next.js integration instead — its split-URL redirects happen on the server, before the page loads.

bash
npm install @testa-soft/react

Requires React 18 or newer.

1. Provider

Wrap your app once, at the root:

tsx
// src/main.tsx
import { createRoot } from "react-dom/client";
import { TestaProvider } from "@testa-soft/react";
import App from "./App";

createRoot(document.getElementById("root")!).render(
  <TestaProvider projectId="{YOUR_PROJECT_ID}">
    <App />
  </TestaProvider>,
);

Done. That's a working integration.

<TestaProvider/> loads your experiments, assigns the visitor to a variation, applies HTML changes, performs split-URL redirects, tracks goals, and reports which variation the visitor saw. Put it above your router — React Router, TanStack Router, Wouter and plain history.pushState all work.

HTML tests

Experiments built in the visual editor (HTML tests) apply automatically — no code. Changes are kept in place when React re-renders the element, and undone when the visitor navigates to a page the experiment doesn't target.

To stop visitors seeing the original content flash before the variant, the provider briefly hides the page on first load until the change is applied. It reveals itself after 4 seconds regardless, so a failed load can't leave a page blank — and once Testa knows your project has nothing to apply, it stops hiding altogether. Turn it off with shield={false}.

Goals

Create goals in the dashboard (Goals tracking). Page-view and click goals need no code. Custom-event goals fire from yours:

ts
import { pushEvent } from "@testa-soft/react";

pushEvent("signup_completed", { plan: "pro" });

Also window.testa.pushEvent(...) and window.Analytica.pushEvent(...). Each goal counts once per visitor.

Analytics events

Whenever a visitor is shown a variation, Testa fires variation_applied in the browser — subscribe to forward it wherever your analytics live.

Subscribe from a component, in an effect. onVariationApplied returns its own unsubscribe function, so returning it from the effect is the whole cleanup — without that, Fast Refresh and StrictMode's double mount stack duplicate handlers:

tsx
// src/components/TestaAnalytics.tsx
import { onVariationApplied } from "@testa-soft/react";
import { useEffect } from "react";

export function TestaAnalytics(): null {
  useEffect(
    () =>
      onVariationApplied((data) => {
        // Send `data` wherever your analytics live.
        console.log(data);
        // {
        //   project_id: 123,                      // your project ID
        //   experiment: 456,                      // experiment ID
        //   variation: 1,                         // variation ID (0 = control)
        //   uuid: "0198f2c1-4b7a-7f3e-9d21-...",  // the visitor's unique ID
        //   title: "Homepage Hero Test",          // experiment name, when set
        //   url: "https://example.com/pricing",   // page the variation applied on
        // }
      }),
    [],
  );
  return null;
}

Render it once, anywhere inside <TestaProvider/>.

experiment and variation are the identifiers you see in the dashboard — variation 0 is always the control. title is present only when the experiment has a name. Forward the whole object, or pick fields out of it:

tsx
// `track` here is whatever you already use — your analytics SDK, or your own fetch.
useEffect(
  () =>
    onVariationApplied((data) =>
      track("Experiment Viewed", {
        experiment: data.experiment,
        variation: data.variation,
        name: data.title,
      }),
    ),
  [],
);

// Or forward the payload as it comes.
useEffect(() => onVariationApplied((data) => track("Experiment Viewed", data)), []);

Register as many handlers as you like. Two guarantees make this safe to wire into a tag manager:

  • A handler registered after the event fired still receives it, so a slow-loading analytics script never misses an exposure.
  • Each handler sees a given experiment/variation once, so re-renders and client-side navigations can't double-count.

Outside a bundle — GTM Custom HTML, or any plain <script> — use the global instead. (window.testa is also importable as import { testa } from "@testa-soft/react" if you prefer a namespace to named imports; it is the same two functions.)

html
<script>
  window.testa.onVariationApplied(function (d) {
    // send d to your analytics
  });
</script>

GTM dataLayer — pushed automatically on every exposure, no setup:

js
window.dataLayer.push({
  event: "Analytica", // the trigger name
  ExperimentId: 456,
  ExperimentName: "Homepage Hero Test", // "" when the experiment has no name
  VariationId: 1,
  VariationName: "Variation1", // "Control" for variation 0
});

Add a Custom Event trigger on Analytica.

The bucketing moment

onVariationAssigned fires when the visitor is bucketed, with the same payload and the same subscribe-in-an-effect shape:

tsx
import { onVariationAssigned } from "@testa-soft/react";

useEffect(() => onVariationAssigned((data) => track("Experiment Assigned", data)), []);

It fires at decision time, before a split-URL redirect leaves the page — so it's the only event a visitor who gets redirected triggers on the original page. variation_applied is the one to use for exposure tracking: it fires once the visitor actually sees the variation, on the page they end up on.

Options

tsx
<TestaProvider
  projectId="{YOUR_PROJECT_ID}"
  cookieDomain=".example.com" // share assignments across subdomains
  shield={false}              // don't hide the page while variants load
>
PropDefaultDescription
projectId—Your project ID.
shieldtrueHide the page until HTML changes are applied. false to disable.
cookieDomain—Cookie domain, e.g. .example.com, to keep assignments across subdomains.
secureCookiestrueSet to false when testing on plain http://localhost.
trackingtrueReport exposures to Testa. false to test without affecting results.