--- title: Implement A/B Testing category: CMS Features order: 2 status: published summary: Configure A/B Testing variants in React Bricks, resolve the active variant in middleware, and fetch the right page variant in Next.js. estimatedTime: 12 min keywords: ab testing a/b testing variants multischeduling middleware analytics next.js app router --- React Bricks A/B Testing uses page variants. Editors create multiple variants of the same page, assign weights to active variants, and React Bricks serves them according to those weights. In code, your app needs to: - resolve the active variant for each request - keep the same visitor on the same variant - pass the selected variant to `fetchPage` - track variant impressions in your analytics provider Docs reference: [A/B Testing and Multischeduling](https://docs.reactbricks.com/cms-features/ab-testing-multischeduling/) ## Configure variants in React Bricks Open the page you want to test in React Bricks and create the variants you need. Variants can be used for two related workflows: - multischeduling, where each variant has its own publish and unpublish window - A/B Testing, where more than one variant is active at the same time and each active variant has a weight For example: | Variant | Weight | | ------- | ------ | | A | 50 | | B | 50 | This creates an even split. To run a `70 / 30` test, set the variant weights accordingly. ## Add the A/B Testing middleware The middleware resolves the active variant for the incoming request and stores it in a cookie, so a visitor keeps seeing the same variant across requests. In current Next.js App starters, `middleware.ts` can chain A/B Testing with i18n: ```ts import { NextResponse } from 'next/server' import { chain, createI18nMiddleware, createWithAbTestingMiddleware, } from 'react-bricks/rsc' import { i18n } from '@/i18n-config' import { abTestingEnabled, getConfig } from './react-bricks/getConfig' const rbConfig = getConfig() const withAbTestingMiddleware = createWithAbTestingMiddleware({ i18n, config: rbConfig, }) const withI18nMiddleware = createI18nMiddleware({ i18n, NextResponse }) const middleware = abTestingEnabled ? chain([withAbTestingMiddleware, withI18nMiddleware]) : withI18nMiddleware export default middleware export const config = { // Matcher ignoring `/_next/` and `/api/` matcher: [ '/((?!api|_next/static|_next/image|favicon.ico|admin|logo.svg|bricks-preview-images|masks|preview).*)', ], } ``` Use `createWithAbTestingMiddleware` in Next.js App Router projects where A/B Testing must work together with i18n. See the middleware reference docs for the details: - [`createWithAbTestingMiddleware`](https://docs.reactbricks.com/api-reference/utilities/create-with-ab-testing-middleware/) for Next.js App Router projects - [`createAbTestingMiddleware`](https://docs.reactbricks.com/api-reference/utilities/create-ab-testing-middleware/) for Next.js Pages Router and Astro projects ## Fetch the selected variant In your App Router page, read the selected variant from the cookie and pass it to `fetchPage` as `variantName`. ```tsx title="app/[lang]/[[...slug]]/page.tsx" import { cookies } from 'next/headers' import { fetchPage, getAbTestingCookie, types } from 'react-bricks/rsc' import config from '@/react-bricks/config' const getData = async ( slug: string | string[] | undefined, locale: string ): Promise<{ page: types.Page | null variantName?: string testName?: string }> => { const cleanSlug = !slug ? '/' : typeof slug === 'string' ? slug : slug.join('/') const cookieStore = await cookies() const variantName = getAbTestingCookie({ slug: cleanSlug, locale, cookieStore, }) const page = await fetchPage({ slug: cleanSlug, language: locale, variantName, config, fetchOptions: { next: { revalidate: 3 } }, }).catch(() => null) return { page, variantName, testName: `${cleanSlug}_${locale}`, } } ``` `getAbTestingCookie` reads the variant selected by the middleware. Passing that value to `fetchPage` keeps the rendered content aligned with the visitor's assigned variant. Docs reference: [`getAbTestingCookie`](https://docs.reactbricks.com/api-reference/utilities/get-ab-testing-cookie/) ## Track the experiment React Bricks decides which variant should be rendered, but your analytics provider measures impressions. The Next.js App Router starter defines a small `GAExperimentTracker` component for this. This is not a React Bricks exported component. It is a project component that sends an `experiment_impression` event to Analytics with the `exp_variant_string` parameter, composed from the experiment name and variant name. ```tsx title="components/GAExperimentTracker.tsx - Next.js App Router example" 'use client' import { sendGTMEvent } from '@next/third-parties/google' import { useEffect } from 'react' interface GAExperimentTrackerProps { testName: string variantName: string } export default function GAExperimentTracker({ testName, variantName, }: GAExperimentTrackerProps) { useEffect(() => { sendGTMEvent({ event: 'experiment_impression', exp_variant_string: `${testName}.${variantName}`, }) }, [testName, variantName]) return null } ``` Then render the tracker when both identifiers are available: ```tsx { testName && variantName && ( ) } ``` With this setup, Analytics receives an `experiment_impression` event for the rendered variant. The `feature/ab-testing` branch of the React Bricks starters repository includes a complete implementation example, including middleware setup and analytics integration: [reactbricks-starters feature/ab-testing](https://github.com/ReactBricks/reactbricks-starters/tree/feature/ab-testing/apps) There is also a concrete Next.js App example that sends the test name and variant name to Google Analytics: [Next.js App analytics example](https://github.com/ReactBricks/reactbricks-starters/blob/03ea4d534b518ab4ec0598624f97dd6b574a09ae/apps/nextjs-app/app/%5Blang%5D/%5B%5B...slug%5D%5D/page.tsx#L139) ## Check the result After wiring A/B Testing: - create two active variants for a page - set variant weights in React Bricks - visit the page in a private browser session - confirm the middleware stores a stable variant cookie - refresh and confirm the same variant is served - verify the selected `variantName` is passed to `fetchPage` - check that Analytics receives an `experiment_impression` event with the correct `exp_variant_string` ## Related docs - [A/B Testing and Multischeduling](https://docs.reactbricks.com/cms-features/ab-testing-multischeduling/) - [Implement A/B Testing](https://docs.reactbricks.com/common-tasks/implement-ab-testing/) - [createWithAbTestingMiddleware](https://docs.reactbricks.com/api-reference/utilities/create-with-ab-testing-middleware/) - [createAbTestingMiddleware](https://docs.reactbricks.com/api-reference/utilities/create-ab-testing-middleware/) - [getAbTestingCookie](https://docs.reactbricks.com/api-reference/utilities/get-ab-testing-cookie/) - [fetchPage](https://docs.reactbricks.com/api-reference/utilities/fetch-page/)