Skip to main content
This guide sets up the headless SDK on a Hydrogen storefront, with test groups assigned on the server. 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. Two steps depend on the @shopify/hydrogen version in your package.json, because Hydrogen 2026.10 changed its analytics and moved cart changes to the browser. Each of those steps has a tab per version.

Setup

Step 1: Install the package

Step 2: Create the client on the server

Import createABConvert from @abconvert/headless/hydrogen, on Hydrogen 2024.4 or later. It tracks nothing until the visitor answers Shopify’s cookie banner, and sets a cookie only when one changed, such as on a first visit.
Server

Step 3: Load it in the root loader and pass it to the browser

The root loader creates the client, adds its cookies to the response, and returns its page state:
Server (loader)
A route that renders a test’s data reads it in its loader and returns getPageState() too. The state then carries that test as the visitor saw it, and the route’s component reads it through the provider:
Server (loader)
Then wrap your routes in the provider:
Keep Hydrogen’s ShopifyScripts in your document <head>, where Hydrogen’s template already renders it.
Component
The provider listens to Hydrogen’s own analytics, so the SDK tracks page views, product views, and add to cart with no more code, and it reads the visitor’s cookie consent from Shopify’s Customer Privacy API, so nothing is sent before the visitor answers your banner. Hydrogen passes events on only after the visitor consents: on 2026.10 and later it keeps the events from before and passes them on then; before 2026.10 it passes on the current page view, and the product view on a product page, and an add to cart made before the answer is not counted.
If your storefront sets a Content Security Policy, add ABConvert to its connectSrc, or the browser blocks everything the SDK tracks. Hydrogen’s 2026.4 template sets one in app/entry.server.tsx:
Server

Step 4: Write the cart attributes

Checkout applies a test only when the cart carries the visitor’s test groups.
The cart changes through Hydrogen’s cart store in the browser. Add ABConvert’s cart line attributes to your add to cart form. The form imports useProductForm from the module where you call createProductComponents, and reads the attributes after the page loads, so a page in the full-page cache stays the same for every visitor:
Component
Then render ABConvertCartAttributes once, inside both CartProvider and ABConvertProvider. It writes ABConvert’s cart attributes on every page and after every cart change, keeps the cart’s other attributes, and tries again when an update fails. Checkout charges what the cart attributes say, so hold your checkout button until useCartReadyForCheckout returns true. Pass both the useCart that createCartComponents returns:
Component
The button waits for a moment after the page loads and while an update runs. We trade that wait for the right price at checkout.
Connect the cart covers 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 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

  • Returning getClientState() from a loader. It holds the visitor, so the route renders per visitor and cannot be cached. The fix: return getPageState(), as the loaders above do.
  • Opting a tested route into Oxygen’s full-page cache. The cache then serves one visitor’s test price to everyone. The fix: call keepPrivateWhenTested on that route.
  • Using ShopPayButton with product variant IDs. It creates a checkout without your cart and accepts no attributes, so the test does not apply. The fix: use a checkout URL or a “Buy now” cart.

Next steps