SPA Issues
In a single-page application (SPA), the browser does not perform a full page load when users navigate between routes. Instead, JavaScript updates the URL and re-renders content. This can cause A vs B experiments to stop working after the first navigation — either the variation disappears, the experiment does not re-trigger, or events stop firing.
Symptoms
- The experiment variation is visible on the initial page load, but disappears after clicking a link or navigating to another route.
- Experiment data accumulates only on the first page visit, not on return visits to the same route via SPA navigation.
- URL targeting that works on a full reload does not trigger on in-app navigation.
- Pageview metrics do not fire on SPA route changes.
Enable SPA support
Go to Project Settings
Click the Configuration tab
Enable SPA support
How SPA support works
When SPA support is enabled, the snippet patches two browser APIs to intercept navigation events:
history.pushState()— called when a SPA navigates forward to a new route.history.replaceState()— called when a SPA replaces the current route without adding to the browser history.
The snippet also listens for the popstateevent, which fires when the visitor presses the browser's back or forward button.
After each navigation event, the snippet re-evaluates all running experiments against the new URL. Experiments whose targeting rules match the new URL are activated; experiments that no longer match are deactivated (their CSS is removed). This means:
- An experiment targeting
/pricingwill activate when the visitor navigates to/pricingand deactivate when they navigate away from it. - An experiment targeting
/with a Substring match will remain active across all routes, because every URL contains/.
Framework-specific notes
React Router
React Router uses history.pushState() for all navigation. Enabling SPA support in project settings is sufficient.
Next.js
Next.js App Router uses history.pushState() for client-side navigation. Enabling SPA support is sufficient. Note that Next.js also supports server-side rendering, so the initial page load is a full server render — the snippet behaves correctly for both.
Vue Router
Vue Router in history mode uses pushState() and is fully supported. Vue Router in hash mode (URLs like example.com/#/about) uses the hashchange event instead of pushState(). SPA support does not automatically detect hash-based routing. See the hash routing section below.
Hash-based routing
If your application uses hash-based routing (the URL contains #before the path), the snippet's SPA support does not detect navigation automatically. As a workaround:
- Use Substring match for URL targeting so the experiment stays active regardless of hash changes.
- Or manually trigger experiment re-evaluation by listening for
hashchangeevents in your variation code.
1// In your variation code — re-check targeting on hash changes2window.addEventListener('hashchange', function() {3 // Re-apply your variation logic if needed4 var currentHash = window.location.hash;5 if (currentHash === '#/pricing') {6 // apply variation7 }8});Experiments deactivating on navigation
When a visitor navigates to a page that no longer matches the experiment's targeting rules, the experiment deactivates. This is expected behaviour — the experiment should only run on the pages it is targeting. The variation's CSS is removed from the page and the original appearance is restored.
If you do not want the experiment to deactivate on navigation away from the target page, you have a few options:
- Broaden the URL targeting to include the pages you want the variation to persist on.
- Use JavaScript in your variation code to make changes that are not purely CSS — for example, modifying the DOM or storing a flag in sessionStorage that persists across navigations.
/checkout and the visitor navigates to /home, the checkout variation correctly stops applying. If you want the variation to persist, widen the URL targeting rule — for example, change it to a Substring match on / to cover all pages.avsb.getVariation('exp_your_id')If it returns
null, the experiment is not matching the current URL. Compare the URL in the address bar to the targeting rule in Step 1 of the experiment builder.