Skip to main content
This guide sets up the headless SDK on a Next.js storefront, with test groups assigned in the middleware. When you finish, every page shows the visitor their test group’s prices, checkout charges them, and your results fill. Read Before you start first. The SDK supports the App Router on Next.js 14.2.10 and later 14.2 releases, 15.1 to 15.4, and 16. Earlier 14 releases, 15.0, and 15.5 are not supported.

How it works

Next.js caches the pages it renders. A page a price test changes, such as a product page with a test price, needs one cached copy per test group. The SDK’s middleware works out the visitor’s test groups and rewrites a tested page to a route named for them, the page key, so Next.js keeps one copy per combination of test groups and serves each visitor the right one. The address bar, your links, and search engines keep seeing the clean URL. Only a price test enters a page key: shipping, offer, and checkout tests apply in checkout, and a URL redirect test redirects before any rewrite. Next.js caching and CDN explains the mechanism and the CDN options; you need none of it to set up.

Setup

Step 1: Install the package and add the middleware

Keep the SDK’s options in one file. The middleware, your layouts, and your cart action all use them.
lib/abconvert.ts
The middleware assigns the visitor, sets the SDK’s cookies, redirects for a URL redirect test, and rewrites tested pages. Keep route handlers such as webhooks, and anything under /api, out of its matcher.
middleware.ts
Do not add the missing rule for the next-router-prefetch header that Next.js’s own middleware guide shows. With it, a link’s prefetch skips the middleware, and the click shows the original prices from that prefetched copy.

Step 2: Add the tested routes

Put the pages a price test can change under app/abconvert/[pageKey]/, and keep them at their usual routes too: the home page, collection and search pages, and product pages. The middleware sends a visitor to the page key route only when a test changes that page for them. The cart and account pages are never rewritten.
Each route group’s layout creates the client from its page key and hands the page state to ABConvertProvider, the SDK’s React context provider. In the browser it reads the visitor from their cookies and tracks for them, and every client component under it reads the client through useABConvert(), so no 'use client' file of your own is needed.
A page is one component under both routes. Each route file passes its params and keeps its own segment config, which Next.js reads from the route file itself.
Keep revalidate on both route files so the page stays static, with one cached copy per page key. ProductPrice and AddToCartButton are the client components from Step 4; they record what the visitor saw and did.

Step 3: Write the cart attributes

Checkout applies a test only when the cart carries the visitor’s test groups. Your add-to-cart server action creates the client without pageKey, so it reads the visitor from the request. It adds the line with the SDK’s line attributes, then writes the SDK’s cart attributes with the cart’s own:
app/actions.ts
Run the same attribute update on your cart page when the attributes changed. Connect the cart covers that, express checkout, and prices by country.
This step is required. Without it, checkout charges the original price and the order is not counted in your test.

Step 4: Track page views, add to cart, and exposures

Next.js has no built-in storefront events, so your client components call track for them, and trackExposure once a test price is on screen. Render TrackPageView once in each route group’s layout, inside the provider.
See Tracking and cookies for offer exposures, custom events, and consent.

Step 5: Confirm it works

Open a test’s preview link on your storefront with a test group forced. You see that test group’s version of the page. Then follow Check the integration to confirm checkout applies the test.
Results need visitors. After you launch, give the test one to two weeks before you read them.

Common mistakes

  • Creating the client without pageKey in a layout or page. It reads the request, so the page renders per request and Next.js caches none of them. The fix: pass pageKey in layouts and pages, and create the client from the request only where you need the visitor, such as your cart action.
  • Calling a cart method on a client made with pageKey. That client has no visitor, so getCartAttributes(), getCartLineAttributes(), and getCheckoutUrl() throw. The fix: create the client without pageKey in a server action or route handler. In the browser, useABConvert() reads the visitor from their cookies, so getCheckoutUrl() works in a client component.
  • Rendering the provider in the root layout. It has no page key, so the page key route’s provider nests under one with the empty key. The fix: one provider per route group, as in Step 2.
  • Building canonical URLs from the rewritten path. The canonical then points to /abconvert/..., which redirects. The fix: build canonical URLs, hreflang links, and structured data from your route params, such as handle. usePathname() in a client component already returns the URL in the address bar.
  • Replacing the middleware’s cache-control: private. A proxy or CDN rule that sets public on every product page makes your CDN serve one test group’s prices to every visitor. The fix: keep private on every response that has it. See Next.js caching and CDN.

Next steps