Set up A/B tests on your website
Install the Web SDK, connect your experiments, and show a variant without the original flashing first.
This guide is for the developer who puts an experiment on a website. It covers installing the Web SDK, connecting an experiment you created under A/B Test, showing a variant without the original flashing first, and checking that it all works. Everything here applies to Web SDK 1.0.115 and later.
Two kinds of experiments
- Visual experiments — you change text, styles, images or elements in the Visual Editor, without code. The SDK applies the change for visitors in the Test group.
- Code experiments — your own code asks which group the visitor is in and renders the matching experience.
Both use the same experiment, the same groups and the same results. Creating the experiment and reading its results is covered in Experiments and Metrics.
Before you start#
- Your SDK key.
- An experiment under A/B Test → Experiments, and its experiment ID — copy it from the Setup tab.
- A way to edit the
<head>of your pages. A tag manager can load the SDK, but it runs too late to prevent a flicker (see below).
Install the SDK#
Add the SDK tag to every page, in <head>:
<script defer
src="https://cdn.jsdelivr.net/gh/B2M-Team/b2m-web-sdk-dist@1/b2m-web-sdk.min.js"
data-api-key="YOUR_SDK_KEY"></script>@1 is the latest 1.x release. The tag does not block the page: with defer (or async) the browser keeps rendering while the SDK downloads, and the SDK starts once the document is ready. window.b2mReady is a promise that resolves when it has.
Check the version
Open the browser console on your site and run b2mWebSDK.version. It should print 1.0.115 or higher. An older number means the page is still loading the SDK from an old address.
Visual experiments#
A visual experiment is designed in the Visual Editor on your own live page and saved with the experiment. On your site, the SDK only looks up the experiments the page lists.
List the experiment on your site#
Put the experiment ID on the SDK tag. Several IDs are separated by commas.
<script defer
src="https://cdn.jsdelivr.net/gh/B2M-Team/b2m-web-sdk-dist@1/b2m-web-sdk.min.js"
data-api-key="YOUR_SDK_KEY"
data-visual-experiment-ids="EXPERIMENT_ID"></script>If you cannot change the tag's attributes, set the list in a small script before the SDK tag instead:
<script>
window.b2mConfig = window.b2mConfig || {};
window.b2mConfig.visualExperimentIds =
(window.b2mConfig.visualExperimentIds || []).concat(['EXPERIMENT_ID']);
</script>An experiment that is not listed assigns nobody
If the ID is missing from the page, the SDK never asks about the experiment: it stays empty in the results even though it is live. Edits you save in the Visual Editor later reach visitors on their own — the list only has to change when you add or remove an experiment.
Where the experiment runs#
URL targeting is set in the experiment's setup. Each rule includes or excludes pages, and an exclude rule always wins over an include rule.
| Match type | How it compares | Example |
|---|---|---|
| Simple | * matches anything. The query string is ignored unless the pattern contains ?; a trailing slash and the #fragment are always ignored. | https://www.example.com/product/* or, on any host, /product/* |
| Exact | The URL must be equal, without wildcards. | https://www.example.com/cart |
| Contains | The pattern appears anywhere in the URL, in any letter case. | /campaign |
| Regex | A JavaScript regular expression, tested against the whole URL. | -c[0-9]{4,}(\?|$) — addresses ending in -c and four or more digits |
Write {4,} — not {4,*}
“Four or more” is {4,}. {4,*} is not a quantifier in JavaScript: the pattern is accepted, shows no error and then matches no real address. Use the Test a URL field in the setup to check a rule against a page before you launch.
Who enters the experiment#
With every assignment request the SDK sends the visitor's timezone, device_type (Mobile, Tablet or Desktop), language, os and browser. The experiment's target audience is checked against these properties, and against any you pass yourself in a code experiment.
A group has one set of changes, and it applies on every device. To test a different change on phones than on desktops, create two experiments and target each at its device.
Design and preview#
- In the Visual Editor, Desktop, Tablet and Mobile load your page at that width, so you see the layout your site really shows at that size. The browser itself stays the same: content your site only serves to a phone's browser does not appear. On a site that forbids showing its own pages in a frame, the editor can only scale the page down instead.
- To look at a group on the live site before launch, open a matching page with
?b2m_preview=EXPERIMENT_ID:GROUP_ID. The group's changes are applied for that tab, nobody is assigned and nothing is counted.?b2m_preview=offends the preview.
Show the variant without a flicker#
The SDK loads without blocking the page, so the page paints first and the variant is applied a moment later. A visitor in the Test group sees the original, then watches it change. That is the flicker.
Two things remove it. The first needs nothing from you: a returning visitor's last variant is applied as soon as the SDK starts, without waiting for the network (the SDK still re-checks in the background). The second is a small snippet you add to the page.
Add the anti-flicker snippet as the first script in <head>
Open b2m-anti-flicker.min.js and paste its contents inline, in a
<script>at the top of<head>. It must be inline: loading it from a file would be one more request before the first paint.The snippet goes firstHTML <head> <script> /* contents of b2m-anti-flicker.min.js — it ends with its options: */ …}(window, document, { timeout: 3000 }); </script> <!-- then the SDK tag --> </head>Tell it what your experiment changes
Replace the options at the end of the snippet with one rule per experiment: its ID, the pages it runs on, and the elements it changes.
The snippet's optionsJavaScript { timeout: 3000, rules: [{ id: "EXPERIMENT_ID", // pages the experiment runs on: [0, a regular expression tested against the URL, flags] n: [[0, "^https://www\\.example\\.com/product/", ""]], // pages to leave out (same form), usually empty x: [], // the elements the experiment changes: the selectors shown in the editor's Changes list s: ["#add-to-cart", ".product-title"] }] }With a rule, a visitor's first page view already hides exactly those elements, on exactly those pages, until their group is applied. Nothing else on the page waits, and no other page is touched.
Let the snippet give up
Add
onerrorto the SDK tag, so that a blocked or failed SDK shows everything at once instead of after the timeout.The SDK tagHTML <script defer src="https://cdn.jsdelivr.net/gh/B2M-Team/b2m-web-sdk-dist@1/b2m-web-sdk.min.js" data-api-key="YOUR_SDK_KEY" data-visual-experiment-ids="EXPERIMENT_ID" onerror="window.b2mAntiFlicker&&b2mAntiFlicker.done('sdk-error')"></script>
What is hidden, and for how long#
| Visitor | What is hidden | Until |
|---|---|---|
| First visit | On a page the experiment runs on: the elements in its rule. On every other page: nothing. | The SDK has loaded, read the experiment and applied the visitor's group. |
| Returning visit | Only what can still change for that visitor. In the Control group, or outside the experiment: nothing. | The SDK starts and applies their last variant. |
| Any visit | Nothing is ever hidden longer than timeout. | — |
The snippet never hides the page itself unless you ask for it. It also never hides anything in the Visual Editor or in a preview.
| Option | Default | What it does |
|---|---|---|
timeout | 3000 | Milliseconds after which everything is shown, whatever happened. |
rules | none | One entry per experiment, as above. Update it when you add an experiment or the experiment starts changing other elements; an outdated rule only means those elements may flicker on a first visit. |
firstVisit | 'off' | Extra hiding for a first visit: a selector list such as '.hero, #cta', or 'page' to hide the whole page until the SDK is done. |
urls | all pages | Regular expressions; firstVisit only applies on pages that match one. |
nonce | none | Your Content-Security-Policy nonce, for the style element the snippet adds. |
If you load the SDK only after cookie consent
Then run the snippet only for visitors who have already accepted. For everyone else the SDK is not coming, and the snippet would hide the elements for its whole timeout for nothing.
A tag manager is too late for this
A tag manager runs after the page has painted. The snippet still helps there, but it cannot prevent the first paint. For no flicker at all it has to be in the page's own HTML.
Code experiments#
When your own code renders the variant, ask the SDK for the visitor's group and compare the group ID with the IDs from the experiment's Setup tab.
await window.b2mReady;
const EXPERIMENT = 'EXPERIMENT_ID';
const TEST_GROUP = 'TEST_GROUP_ID';
const [assignment] = await b2mAssignExperiment([EXPERIMENT], { plan: 'premium' });
if (assignment && assignment.GroupId === TEST_GROUP) {
renderNewCheckout();
} else {
renderClassicCheckout(); // Control, or not in the experiment
}| Function | What it returns |
|---|---|
b2mAssignExperiment(ids, properties?, options?) | A promise of [{ ExperimentId, GroupId, GroupName }] — one entry per experiment the visitor is in. properties are your own targeting values; { forceRefresh: true } asks the service even inside the cache window. |
b2mGetExperimentAssignment(id) | The stored assignment for one experiment, or null. Synchronous. |
b2mGetExperimentGroup(id) | The stored group for one experiment, or null. Synchronous. |
b2mSetUserId(id) | Sets your own user ID after login (null on logout). Assignments follow the user, so a signed-in visitor keeps their group on another device. |
Decide from the answer of b2mAssignExperiment. The two synchronous helpers return what is stored, which can be one page view behind a decision taken on the platform.
The first time a visitor is shown a group in a browser session, the SDK sends an ab_test_view event with the experiment and the group. For a visual experiment it is sent after the change is on the page, for the Control group too. The experiment's results are built on this event.
When a change on the platform reaches visitors#
| You do this | Visitors get |
|---|---|
| Launch the experiment | Their group, the next time a listed page asks. |
| Pick a winner (complete the experiment) | The winning group, everyone. |
| Stop or archive it | No group: the original experience. |
An answer is reused for 60 seconds, then asked again, so a decision reaches a visitor within a minute of their next page view. In a visual experiment a visitor who had a variant in the last 30 minutes is shown it immediately and corrected when the fresh answer arrives — so right after you stop an experiment, such a visitor may see the variant for a moment once.
Settings#
| Setting | Default | What it does |
|---|---|---|
visualExperimentIds | none | The visual experiments to look up. Also data-visual-experiment-ids on the tag. |
visualExperiments | on | false turns visual experiments off on the page. Also data-visual-experiments="false". |
experimentCacheTtl | 60000 | Milliseconds an answer is reused before the service is asked again. |
experimentInstantApplyMaxAge | 1800000 | Visual experiments: how old a visitor's last variant may be to be applied immediately. 0 always waits for the service. |
experimentTimeout | 1500 | Milliseconds to wait for the service before using the last known answer, when there is one. |
experimentIdentitySource | 'user' | 'user' keys the group on your user ID once it is set; 'fingerprint' always keys it on the device. |
abMode | false | true sends only ab_test_view and user properties — for sites that collect behavioural events elsewhere. |
The two settings with a data- attribute can stay on the tag. For the others, put the configuration and the SDK key in window.b2mConfig before the SDK tag, and leave data-api-key off the tag:
<script>
window.b2mConfig = {
apiKey: 'YOUR_SDK_KEY',
visualExperimentIds: ['EXPERIMENT_ID'],
experimentInstantApplyMaxAge: 0
};
</script>
<script defer src="https://cdn.jsdelivr.net/gh/B2M-Team/b2m-web-sdk-dist@1/b2m-web-sdk.min.js"></script>Check that it works#
Open a page the experiment runs on and use the browser console:
b2mWebSDK.version // "1.0.115" or higher
b2mVisual.status() // mode: "live", experimentIds: […], matched: […], applier: what is applied
b2mAntiFlicker // with the snippet: what it hid, why it stopped ("released") and after how long ("ms")matchedlists the experiment: the page is listed and the URL rules match.- In the Network tab there is one request ending in
/visual-configurationand one to/Experiments/assign. On a return visit within a minute there may be none — the answers are cached. b2mAntiFlicker.releasedisappliedorinstant.timeoutmeans the SDK did not finish in time.
Troubleshooting#
| What you see | Likely cause | What to do |
|---|---|---|
| The experiment is live but nobody enters it | The page does not list the experiment ID, or the URL rules do not match the page. | Check b2mVisual.status(): the ID must be in experimentIds and in matched. |
| The original shows for a moment, then changes | No anti-flicker snippet, or it is not the first inline script in <head>, or its rule does not list the element. | Add the snippet and the rule as described above. |
| An element is missing for a few seconds, then appears unchanged | The snippet waited for an SDK that did not start: wrong or missing key, a blocked request, or no consent. | Look at b2mAntiFlicker.released: timeout, no-init or sdk-error say which. |
| A regex rule matches nothing | Usually {4,*} instead of {4,}, or an unescaped . or ?. | Try the address in Test a URL. |
| A stopped experiment still shows briefly | The visitor had the variant within the last 30 minutes. | It corrects itself on that page view. Set experimentInstantApplyMaxAge: 0 to never show it. |
| Phones should get a different change than desktops | A group has one set of changes for all devices. | Run two experiments, each targeted at its device. |
Next steps#
Screenshots captured from the B2Metric E‑Commerce demo workspace, October 2026.