Features
A/B Experiments
Split-test anything — copy, layouts, pricing, flows — and measure which variant wins. Flowgrid handles weighted assignment and sticky persistence per visitor through the fg.experiments engine, while exposure and conversion are logged with fg.track(). There are two ways to set this up — a no-code path for marketers (one script tag + the dashboard) and a developer path in code. Both are covered below.
Overview
An experiment has a stable experimentId and two or more weighted variants. When a visitor first hits a page running the experiment, the client engine assigns them a variant by weighted random and stores it in localStorage — so the same visitor always sees the same variant. You render that variant, log an exposure when they see it, and a conversion when they complete your goal.
Already initialised?
These examples assume you've created the SDK client. See the SDK Quickstart — in short, const fg = FlowGrid.init({ webId, apiKey }).
Quick copy: the no-build tag (full guide below)
You don't need a bundler — or any JavaScript. Drop in this single tag and FlowGrid self-initialises from data-site and your data-api-key, then automatically runs every experiment you've set up in the dashboard:
1<script src="https://cdn.flow-grid.xyz/flowgrid.min.js" data-site="YOUR_WEBSITE_ID" data-api-key="YOUR_API_KEY"></script>On load it fetches your running experiments, assigns the visitor a variant, and applies that variant's redirectUrl (split-URL test) or declarative changes — with an anti-flicker guard and automatic exposure logging. A marketer configures the whole test in the dashboard; no per-variant code is written. (Opt out with data-no-experiments, or call FlowGrid.instance().experiments.initFromServer() yourself — e.g. after client-side navigation in a SPA.)
Want session replay, autoTrack tuning, user identify, or consent control on this no-build path? Set a config object before the tag — every init() option works declaratively:
1<script>
2 window.FlowGridConfig = {
3 apiKey: "YOUR_API_KEY", // recommended
4 replay: true, // session replay on
5 autoTrack: { heatmaps: true },
6 };
7</script>
8<script src="https://cdn.flow-grid.xyz/flowgrid.min.js" data-site="YOUR_WEBSITE_ID"></script>Prefer full programmatic control? Add data-manual to the tag to skip auto-init and call FlowGrid.init({ webId, apiKey, replay: true }) yourself. Including your apiKey is recommended for authenticated ingestion.
No-code setup (for marketers & CMS)
If you're on WordPress, Wix, Squarespace, Webflow, Shopify or Framer, you don't need a developer or a build step. Add one script tag once, then create and run every test from the FlowGrid dashboard.
1. Add FlowGrid to your site (once)
Paste this into your site's <head>(most CMSs have a "custom code" or "header scripts" box — see the install guides). It self-initialises from data-site — no JavaScript to write. Add your data-api-key too: experiments still assign variants without it, but your exposures and conversions (the results) are only recorded when the key is present — so the key is required to actually measure a test.
1<script src="https://cdn.flow-grid.xyz/flowgrid.min.js" data-site="YOUR_WEBSITE_ID" data-api-key="YOUR_API_KEY"></script>Already on WordPress with the FlowGrid snippet?
If your site already loads the analytics snippet (core.flow-grid.xyz/flowgrid.js), you don't need a second script — just add data-experiments and your data-api-key to that tag and it loads the experiment engine for you:
1<script src="https://core.flow-grid.xyz/flowgrid.js" data-site="YOUR_WEBSITE_ID" data-api-key="YOUR_API_KEY" data-experiments defer></script>2. Create the experiment in the dashboard
In Analytics → Experiments → New Experiment, give it a name, add your variants, and set the traffic split. That's it — the script on your site picks it up automatically on the next page load and starts assigning visitors.
3. Choose what to test
- Split-URL test (no code): give a variant a Redirect URL in the dialog and visitors assigned that variant are sent to that page (e.g.
/home-b). FlowGrid handles assignment, stickiness, the redirect, and exposure for you.
Example — test two landing pages: to pit your current homepage against a new design living at/home-v2, create an experiment called "Homepage redesign", add a variantredesignwith Redirect URL/home-v2, set a 50/50 split, and click Running. Half your visitors now see the new page; the dashboard shows which one converts better. - On-page tweaks (change a headline, hide a banner, swap a colour): a point-and-click visual editor is coming. Until then, these are added by a developer (see the Developer setup).
4. Track the win
FlowGrid counts who saw each variant automatically. To record a conversion (signup, purchase, …) so the dashboard can pick a winner, add a data-fg-goal attribute. It works on a button, a form, or your thank-you page — no JavaScript:
1<!-- count a click as a conversion — no JavaScript -->
2<button data-fg-goal="YOUR_EXPERIMENT_ID" data-fg-metric="signup">
3 Sign up
4</button>More conversion triggers — still no code
Change the trigger with data-fg-on: load counts a page view (put it on a thank-you page's <body>), submit fires on form submit, view when scrolled into view. data-fg-metric names the goal, data-fg-value records a number, and data-fg-goal="*" counts for every test the visitor is in. A point-and-click visual goal picker is still on the roadmap — but these attributes work today.
5. Watch results & ship the winner
Results stream into the experiment's card in the dashboard. When a variant wins, hit Ship — every visitor is moved onto the winner with no code change and no redeploy.
Want replay, heatmaps or your API key on this path?
Set a config object before the tag — every option works without writing an init() call. Including your apiKey is recommended for authenticated ingestion.
1<script>
2 window.FlowGridConfig = {
3 apiKey: "YOUR_API_KEY", // recommended
4 replay: true, // session replay on
5 autoTrack: { heatmaps: true },
6 };
7</script>
8<script src="https://cdn.flow-grid.xyz/flowgrid.min.js" data-site="YOUR_WEBSITE_ID"></script>Notes: add data-no-experiments to skip experiments, or data-manual to turn off auto-init and configure everything in code (see Developer setup). Single-page apps should re-run FlowGrid.instance().experiments.initFromServer() after client-side navigation.
Developer setup — the lifecycle
| Step | Method | Where | What it does |
|---|---|---|---|
| 1 | define() | Admin / server | Register the experiment and its variants once. |
| 2 | init() | Browser | Start the client engine — assigns and persists a variant per visitor. |
| 3 | variant() | Browser | Read the visitor's assigned variant synchronously and render it. |
| 4 | track("experiment_exposure") | Browser / server | Log that the visitor actually saw the variant. |
| 5 | track("experiment_conversion") | Browser / server | Log a success metric (signup, purchase, …) against the variant. |
1. Define the experiment
Register the experiment once (admin setup or a one-off script). Variant weights are relative — 50 / 50 is an even split.
1await fg.experiments.define({
2 experimentId: "checkout_v2",
3 name: "New checkout flow",
4 description: "Single-page vs multi-step checkout",
5 variants: [
6 { id: "control", weight: 50 },
7 { id: "B", weight: 50 },
8 ],
9});2. Initialize the client engine
In the browser, call init() with the experiments you want running. This verifies them, assigns the visitor a variant, and persists it.
1await fg.experiments.init([
2 {
3 id: "checkout_v2",
4 variants: [
5 { id: "control", weight: 50 },
6 { id: "B", weight: 50 },
7 ],
8 },
9]);3. Read the variant & render
variant() returns the cached assignment synchronously (or null if none). Branch your UI on it.
1const variant = fg.experiments.variant("checkout_v2");
2
3if (variant === "B") {
4 renderSinglePageCheckout();
5} else {
6 renderMultiStepCheckout();
7}4. Track exposure
Log an exposure the moment the visitor actually sees the variant — this is what powers your results. The recommended call is fg.track("experiment_exposure", …) with the experiment ID, the user (or anonymous) ID, and the assigned variant.
1await fg.track("experiment_exposure", {
2 experimentId: "checkout_v2",
3 userId: "u_1",
4 variantId: variant,
5});Tip
Fire exposure on render, not on assignment. A visitor assigned a variant they never saw shouldn't count toward the test.
5. Track conversion
When the visitor completes your goal, log a conversion with a metric name and optional value. The recommended call is fg.track("experiment_conversion", …). In the browser, fg.experiments.localConversion()is a convenience shortcut that reuses the visitor's cached variant so you don't have to repeat it.
1// Recommended — explicit experiment_conversion event
2await fg.track("experiment_conversion", {
3 experimentId: "checkout_v2",
4 userId: "u_1",
5 variantId: "B",
6 metricName: "purchase",
7 value: 49.99,
8});
9
10// Browser shortcut — reuses the visitor's cached variant
11await fg.experiments.localConversion("checkout_v2", "purchase", 49.99);Full example
A complete hero-CTA copy test — does "Book a Demo" or "Start Free Trial" drive more sign-ups? This mirrors a real production setup.
1import { FlowGrid } from "flowgrid-sdk";
2
3const fg = FlowGrid.init({ webId: "YOUR_WEBSITE_ID", apiKey: "YOUR_API_KEY" });
4
5const HERO_CTA = "hero_cta_copy";
6const VARIANTS = { control: "book_a_demo", treatment: "start_free_trial" } as const;
7
8// 1–2. Define implicitly + initialize the engine
9export async function initHeroCta() {
10 await fg.experiments.init([
11 {
12 id: HERO_CTA,
13 variants: [
14 { id: VARIANTS.control, weight: 50 },
15 { id: VARIANTS.treatment, weight: 50 },
16 ],
17 },
18 ]);
19}
20
21// 3. Read the assigned variant (falls back to control)
22export function getHeroCtaVariant() {
23 return fg.experiments.variant(HERO_CTA) ?? VARIANTS.control;
24}
25
26// 4. Log exposure when the hero renders
27export async function trackHeroExposure(userId: string) {
28 await fg.track("experiment_exposure", {
29 experimentId: HERO_CTA,
30 userId,
31 variantId: getHeroCtaVariant(),
32 });
33}
34
35// 5. Log conversion on signup
36export async function trackHeroConversion(userId: string) {
37 await fg.track("experiment_conversion", {
38 experimentId: HERO_CTA,
39 userId,
40 variantId: getHeroCtaVariant(),
41 metricName: "signup",
42 });
43}Server-side rendering
To avoid a flash of the wrong variant, hydrate the visitor's assignment from cookies before reading it on the server. Works with Next.js cookies(), a raw Cookie header (Workers, Hono, Bun), or injected values.
1import { cookies } from "next/headers";
2
3// Hydrate once per request, then read synchronously
4fg.experiments.hydrateFromCookies(cookies());
5const variant = fg.experiments.variant("checkout_v2");
6
7// Persist the assignment back to the browser
8res.headers.set("Set-Cookie", fg.experiments.serializeAssignmentCookie());Other runtimes
Outside Next.js use hydrateFromCookieHeader(cookieHeader), or injectAssignments({ checkout_v2: "B" }) when you resolved the variant from your own database.