Run A/B Tests with Storyblok and VWO
Storyblok is the first headless CMS that works for developers & marketers alike.
A/B tests allow marketing teams to evaluate which content variations and user journeys deliver the best user experience and drive conversions.
Storyblok's latest collaboration with VWO's platform lets editors run these tests right from the CMS, combining VWO campaigns with Storyblok’s Visual Editor.
This tutorial demonstrates how to take advantage of this integration to create A/B tests in Storyblok. We will walk you through the setup process, explain how to create banner variations, and render them in a Vue project.
The code in this tutorial is a subset of a complete demo that runs A/B tests using Storyblok's Vue SDK and VWO's Node.js SDK. Check the repository on GitHub for more information.
Prerequisites
- An active VWO account
- A Storyblok space with the VWO App installed.
- A Vue project configured to work with Storyblok's SDK.
To integrate Vue with Storyblok, follow our developers' guide. Alternatively, adapt the code to the frontend framework of your choice.
Create blocks for tests
Let's start by defining the block structure in Storyblok.
Open the Block library, click on + New Block, and create a nestable block named experimentation-vwo
. Add a variations
field to store the alternatives you'd like to test, and save it.
Create another nestable block for the actual tested content. Name this block banner
and add the following fields:
- A
title
Text field - An
image
Asset field - A
vwo_variation
Plugin field. Set its Custom Type tosb-vwo
Now, open the VWO dashboard. Create a new A/B test named "Banner", and add as many variations as you'd like to test.
Add variations to a story
Back in your Storyblok space, open the home story and add the experimentation-vwo
block created in the previous step.
The first time you use the plugin, you must authenticate and provide your VWO account ID and API token.
Add multiple variants of the banner
block to the variations
field. Change the title or the image—depending on what you're testing—and assign each to the VWO Banner feature flag and variation defined in the previous step.
Adapt your frontend code for experiments
Now that your space is ready, add the VWO integration to your code.
First, install VWO's Node.js SDK:
npm install vwo-fme-node-sdk
Next, add the account ID and API token to the .env
file as environment variables named VITE_VWO_ACCOUNT_ID
and VITE_VWO_SDK_KEY
.
Finally, initialize VMO's client:
import { createApp } from 'vue';
import { StoryblokVue, apiPlugin } from '@storyblok/vue';
import App from './App.vue';
import { init } from 'vwo-fme-node-sdk';
import Page from './components/Page.vue';
const app = createApp(App);
app.use(StoryblokVue, {
accessToken: import.meta.env.VITE_STORYBLOK_DELIVERY_API_TOKEN,
apiOptions: {
region: 'eu',
},
use: [apiPlugin],
});
const vwoClient = init({
accountId: import.meta.env.VITE_VWO_ACCOUNT_ID,
sdkKey: import.meta.env.VITE_VWO_SDK_KEY,
});
app.provide('vwoClient', vwoClient);
app.component('Page', Page);
app.mount('#app');
Use Vue's provide
method to ensure the client is available across your project.
Add the components
In the src/components
directory, create two files: a Banner.vue
file, which you can copy and adapt from our demo repository, and the ExperimentationVwo.vue
file to handle the variations.
The following is an abbreviated version of the code found in the demo repository:
<script setup>
import { computed, inject } from 'vue';
import { getUserId } from '../utils/userContext.js';
const props = defineProps({ blok: Object });
const vwoClient = await inject('vwoClient');
const flag = await vwoClient.getFlag(
props.blok.variations[0]?.vwo_variation?.featureKey,
{ id: getUserId() },
);
const selectedUuid = flag.isEnabled()
? flag.getVariable('contentUUID', null)
: null;
const selectedVariations = computed(() => {
const variation =
props.blok.variations.find((v) => v._uid === selectedUuid) ||
props.blok.variations.find((v) => v.vwo_variation?.isDefault);
return variation ? [variation] : [];
});
</script>
<template>
<StoryblokComponent
v-for="variant in selectedVariations"
:key="variant._uid"
:blok="variant"
/>
</template>
We extract the featureKey
from the first variation, call the vwoClient.getFlag
method to get the flag object, and then obtain the selectedVariations
so the StoryblokComponent
helper renders only the ones coming from VWO.
VWO requires a unique user ID. How you generate this string depends on your business logic.
Finally, add both of the components to your application:
import { createApp } from 'vue';
import { StoryblokVue, apiPlugin } from '@storyblok/vue';
import App from './App.vue';
import { init } from 'vwo-fme-node-sdk';
import Banner from './components/Banner.vue';
import ExperimentationVwo from './components/ExperimentationVwo.vue';
const app = createApp(App);
app.use(StoryblokVue, {
accessToken: import.meta.env.VITE_STORYBLOK_DELIVERY_API_TOKEN,
apiOptions: {
region: 'eu', // Choose the correct region from your Space.
},
use: [apiPlugin],
});
const vwoClient = init({
accountId: import.meta.env.VITE_VWO_ACCOUNT_ID,
sdkKey: import.meta.env.VITE_VWO_SDK_KEY,
});
app.provide('vwoClient', vwoClient);
app.component('Banner', Banner);
app.component('ExperimentationVwo', ExperimentationVwo);
app.mount('#app');
Check out our demo repository to see the complete implementation.
Wrap up
Now that you know how to create an A/B test using the VWO App, you can help marketers extend this integration to CTAs, headlines, images, and any other element on the site.
Consult with your content team to understand their priorities, prepare matching block templates, and enable them to create personalized experiences that drive conversions.