Experiments
Storyblok Experiments introduces content variants at the story level. Use experiments for A/B testing or to manage personalized content.
To learn how to set up experiments and link stories from the Visual Editor, follow the Experiments manual.
How experiments work
Section titled “How experiments work”An experiment record connects a set of stories to a set of variants. Each variant accepts a name, a technical name, a weight distribution (as a percentage), a flag indicating whether it's the control, and a list of story mappings that pair a source story with the variant story that replaces it.
The control is the variant designated as the baseline. Always name and treat the main story as the control. The experiments endpoint flags the main story with is_control: true, which maintains consistency between the UI and the API.
When a site visitor loads a story associated with an experiment, the logic you implemented in your code picks one variant according to the configured weights, and then renders the variant’s mapped story.
Integrate experiments
Section titled “Integrate experiments”To integrate experiments into the code, follow these steps:
- Fetch a story.
- Fetch experiments and determine whether any active experiment targets the current story.
- Pick a variant based on the variants’ weights, respecting the visitor's prior assignment.
- Fetch the variant story referenced in the variant’s story mappings and render it.
- Persist the chosen variant for the visitor.
- Push the experiment's analytics data to Storyblok regularly.
Fetch experiments
Section titled “Fetch experiments”Retrieve the list of active experiments using the Content Delivery API's experiments endpoint.
{ "experiments": [ { "id": 176070002766742, "name": "a_simple_experiment", "display_name": "A simple experiment", "story_ids": [176024833123843], "variants": [ { "name": "control", "display_name": "Control", "public_id": "var_uhjedoghzx68", "weight": 70, "is_control": true, "story_mappings": [] }, { "name": "test", "display_name": "Test", "public_id": "var_xhicas8uin5l", "weight": 30, "is_control": false, "story_mappings": [ { "original_story_id": 176024833123843, "original_slug": "home", "variant_story_id": 176070141098149, "variant_slug": "home-a_simple_experiment-test" } ] } ] } ], "cv": 1778675529}The following example determines whether an active experiment includes the current story:
import { storyblokInit, apiPlugin } from '@storyblok/js';
const { storyblokApi } = storyblokInit({ accessToken: 'YOUR_DELIVERY_API_TOKEN', use: [apiPlugin],});
// Fetch storyconst { data: storyData } = await storyblokApi.get('cdn/stories/home', { version: 'published',});
let story = storyData.story;
// Fetch experimentsconst { data: experimentsData } = await storyblokApi.get('cdn/experiments');
const experiments = experimentsData.experiments;
// Determine whether an active experiment includes the fetched storyconst experiment = experiments.find((experiment) => experiment.story_ids.includes(story.id),);Choose a variant
Section titled “Choose a variant”Set the variant weights in the Storyblok UI. The sum of all distribution percentages must equal 100%. and sum to 100. A draw against a uniform random number in that range maps to a single variant. Consider the following example:
const pickVariant = (variants) => { const total = variants.reduce((acc, variant) => acc + variant.weight, 0); let random = Math.random() * total; for (const variant of variants) { random -= variant.weight; if (random <= 0) return variant; } return variants[variants.length - 1];};You can implement similar experimentation logic on the server or at the edge.
Render a variant
Section titled “Render a variant”When the chosen variant is the control, render the main story as-is.
When the chosen variant is another variant, look up the entry in the variant’s story mappings that matches the current story’s full_slug.
Fetch that variant story from the Content Delivery API, and render it instead of the main story:
if (experiment) { const chosenVariant = pickVariant(experiment.variants);
// Unless the chosen variant is the control, its slug differs from the main story if (!chosenVariant.is_control) { const mapping = chosenVariant.story_mappings.find( (mapping) => mapping.original_slug === story.full_slug, ); if (mapping) { // Fetch the variant story const { data: variantData } = await storyblokApi.get( `cdn/stories/${mapping.variant_slug}`, { version: 'published' }, ); // Overwrite the main story data with the variant story data story = variantData.story; } }}Persist a variant
Section titled “Persist a variant”To measure consistent behavior over time, a visitor who loads the same story should receive the same variant.
A common method is to store the variant ID in a cookie keyed by the experiment ID. This persists the assignment per visitor and per experiment, not per story. Subsequent requests reuse the cookie to render the associated story’s variant.
Webhooks
Section titled “Webhooks”Experiments provide the following webhook triggers:
- Experiment created
- Experiment started
- Experiment paused
- Experiment resumed
- Experiment winner selected
- Experiment completed
- Experiment deleted
Learn how to configure webhooks and explore example payloads in the webhooks concept.
Use webhooks for actions like toggling experimentation logic, clearing the cache, or rebuilding the site.
View results
Section titled “View results”Use an analytics platform to collect performance data for an active experiment and load it into Storyblok with the Management API’s experiments results endpoint.
Storyblok displays the data in Labs → Experiments → Experiment name → Results, where editors can monitor it without leaving the CMS.
Further resources
Section titled “Further resources”Was this page helpful?
This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window) . Terms of Service (opens in a new window) apply.
Get in touch with the Storyblok community