Skip to Content

Getting started

Nuxt

Run A/B tests in Nuxt server middleware, so the variant is decided before the page renders. No layout shift.

2 min read

Within the browser the Vue SDK will do everything you need it to do. If you are only running your A/B tests client side, you don't need this package. However Nuxt also renders on the server, where you might also want to decide a test.

This package is for Nuxt's server (Nitro) only. On React Router, and on Shopify Oxygen, the loader decides instead: see React Router.

When to use

A good scenario to use this is when you want to A/B test part of a single page, above the fold. Like on the homepage of Improve. With a client side test there would be layout shift, since the first paint has to be the same for everyone. This can have a negative impact on alternative versions. That's why we need a server side test: the server decides what version the visitor gets, and the first paint matches it.

How it works

The Improve Nuxt SDK is a server middleware: it runs before every page renders, decides the variant from cookies or query parameters (or buckets a new visitor), keeps it in cookies, and leaves it on the request for the page.

Unlike the Next.js SDK, it doesn't rewrite to another route. Nuxt hydrates the route the browser shows, so the page renders the variant itself, and the browser reads the same variant from the cookie to record the exposure.

Install

npm i @obelism/improve-sdk @obelism/improve-sdk-nuxt @obelism/improve-sdk-vue

Middleware

Create the middleware by passing it the Improve server SDK and a list of the A/B tests that you want to decide on the server, each with the page route it runs on.

server/middleware/improve.ts
import { ImproveServerSDK } from '@obelism/improve-sdk/server'
import { generateImproveNuxtMiddleware } from '@obelism/improve-sdk-nuxt'

const improveSdk = new ImproveServerSDK({
	organizationId: 'org_…',
	environment: 'production',
	// `imp_…` from Settings → API tokens, as `NUXT_IMPROVE_API_TOKEN`.
	token: () => useRuntimeConfig().improveApiToken,
	configRetries: 0,
})

export default generateImproveNuxtMiddleware({
	improveSdk,
	serverABtests: [{ slug: 'startpage-visual', routeHandler: '/' }],
})

Declare improveApiToken: '' under runtimeConfig in nuxt.config.ts, so the token stays on the server.

Render the variant

app/pages/index.vue
<script setup lang="ts">
import { getImproveContext } from '@obelism/improve-sdk-nuxt'

import { useTestValue } from '~/improve'

const decided = useState('improve:startpage-visual', () => {
	const event = useRequestEvent()
	return (event && getImproveContext(event)['startpage-visual']) || null
})
const variant = useTestValue('startpage-visual', decided.value ?? 'control')
</script>

<template>
	<HeroVariation v-if="variant === 'variation'" />
	<HeroControl v-else />
</template>

useState hands the server's decision to the browser. useTestValue comes from the provider the Vue SDK generates: it renders the server's value until the SDK is set up, then reads the same value from the cookie, which records the exposure. After a client-side navigation no request reached the middleware, so the browser decides.

Keep tested routes out of prerendering and route caches (prerender, swr, isr in routeRules): a cached page shows one variant to everyone.

Example

The Nuxt example puts it together, with tracking.

On this page

Marks in the margin

  • Note
  • Watch out
  • Updated

Scribes marked doubtful lines in the margin. That practice is called obelism.