> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abconvert.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Headless SDK

> Use the headless SDK to run ABConvert tests on a custom storefront built with Hydrogen, Next.js, or the Storefront API.

The headless SDK is an npm package that runs your ABConvert tests on a [storefront you build yourself](https://shopify.dev/docs/storefronts/headless): Hydrogen, Next.js, or any stack on the [Storefront API](https://shopify.dev/docs/api/storefront).

On a custom storefront your code renders every page, so ABConvert cannot change the page for you. The SDK does four jobs instead:

1. **Assign.** It puts each visitor in a [test group](/experiments/overview#2-test-groups-and-traffic-allocation), on your server or in the browser, and keeps them there on every visit.
2. **Read.** It gives your code the visitor's test group, test price, shipping rates, and offers, so you render them.
3. **Connect the cart.** It gives you the cart attributes that make Shopify checkout apply the test, and that tie the order to the test group.
4. **Track.** It records page views, add to cart, and custom events for your results.

You integrate once. After that, price, shipping, offer, checkout, and URL redirect tests need no code change: you create them, launch them, and read results in the ABConvert admin, as on any other store. Only a [feature test](/headless-sdk/feature-tests) (coming soon) needs code, once per feature, because you build its versions.

This page is for developers who build a custom storefront.

## When to use it

* Your storefront runs on Hydrogen and you want to test a \$49 price against \$59 on your best-selling product.
* Your Next.js storefront shows a free shipping bar, and you want to test a \$50 threshold against \$75.
* You want to pair a higher price with free shipping in one [combined test](/experiments/combined-test).
* You run a [checkout test](/experiments/checkout-test) and your cart is created through the Storefront API.
* You built a new hero section in React and want to test it against the current one.

## When not to use it

* **Your storefront uses a Shopify theme.** ABConvert works there with no code. If you need to read test data in theme JavaScript, use the [JavaScript API](/api-reference/browser-api) instead.
* **You want to create tests or read results from your backend.** Use the [REST API](/api-reference/overview) instead.

## What you can test

| Test type | On a custom storefront | How |
| - | - | - |
| [Price test](/experiments/price-test) | Yes | You render the test price. Checkout applies it. |
| [Shipping test](/experiments/shipping-test) | Yes | Checkout applies the rate. You can render rates and the free shipping threshold. |
| [Offer test](/experiments/offer-test) | Yes | Checkout applies the discount. You render the offer message. |
| [Checkout test](/experiments/checkout-test) | Yes | Runs in Shopify checkout. Needs the cart attributes only. |
| [URL redirect test](/experiments/url-redirect-test) | Yes | You redirect in a loader or middleware. |
| [Feature test](/headless-sdk/feature-tests) | Coming soon | You render each test group's version in your own code. |
| [Theme test](/experiments/theme-test), [template test](/experiments/template-test) | No | These need a Shopify theme. |
| [Visual Editor](/experiments/visual-editor-test) | No | Not supported on a custom storefront. |
| [Combined test](/experiments/combined-test) | Yes, for the test types above marked Yes | Each change works as it does in its own test. A theme, template, or Visual Editor change does not apply. |
| [Personalization](/personalization/overview) | Yes, for the same test types | The audience is checked the same way as for a test. See [Targeting](/headless-sdk/targeting). |

## Before you start

You need:

* ABConvert installed on your Shopify store.
* A storefront that creates carts with the [Storefront API](https://shopify.dev/docs/storefronts/headless/building-with-the-storefront-api/cart/manage) and sends visitors to Shopify checkout.
* Your store's `myshopify.com` domain.
* The headless SDK turned on for your store. Ask your ABConvert contact.
* A storefront key, from **Integrations > Headless Storefront** in ABConvert.
* A server on Node.js 20 or later, Oxygen, Cloudflare Workers, or Vercel Edge. On Next.js, see [supported versions](/headless-sdk/nextjs). React 17 or later for the React bindings.
* A test in the ABConvert admin. A test in [preview](/experiments/lifecycle#preview-before-you-launch) is enough to integrate against.

## How it works

Create a client for each request. The SDK loads your tests once per server and shares them, and every read answers from memory.

The recommended setup assigns on the server, so the first HTML already shows the right test group.

1. Your server creates the client from the incoming request.
2. Your loaders and components read the visitor's test config from the client, such as their test group or test price, and render it.
3. Your cart code writes the client's cart attributes to the Storefront API cart.
4. Shopify checkout reads those attributes and applies the test.
5. ABConvert ties the order to the visitor's test group.

The diagram shows one visit, from the first page to the order.

```mermaid theme={null}
%%{init: {"themeVariables": {
  "actorBkg": "#82C621",
  "actorBorder": "#82C621",
  "actorTextColor": "#0F1A05",
  "actorLineColor": "#9AA3AD",
  "signalColor": "#217BD1",
  "sequenceNumberColor": "#FFFFFF",
  "noteBkgColor": "#ACFF17",
  "noteBorderColor": "#ACFF17",
  "noteTextColor": "#0F1A05"
}}}%%
sequenceDiagram
    autonumber
    participant V as Browser
    participant S as Your server<br/>(your code + SDK)
    participant A as ABConvert
    participant SH as Shopify

    V->>S: Open a page
    S->>A: Load tests (cached)
    A-->>S: Tests
    Note over S: SDK assigns<br/>the visitor
    S->>SH: Query products
    SH-->>S: Original prices
    Note over S: Your code renders<br/>the test price
    S-->>V: HTML, SDK state, cookies

    V->>A: Page views, add to cart
    V->>S: Add to cart
    S->>SH: Cart lines and<br/>cart attributes
    SH-->>S: Cart, checkout URL

    V->>SH: Go to checkout
    Note over SH: Checkout reads the<br/>attributes and<br/>applies the test
    SH->>A: Checkout steps, order
    Note over A: Results in<br/>the admin
```

The client on the server, `useABConvert()` in a component, and a browser-only client share one API, with the same names and behavior. The few methods that exist on one side only are marked in the [reference](/headless-sdk/reference).

## Pick your stack

Each guide is a complete setup for its stack, in that stack's code only. Follow one.

<CardGroup cols={3}>
  <Card title="Next.js" href="/headless-sdk/nextjs">App Router, with test groups assigned in the middleware.</Card>
  <Card title="Hydrogen" href="/headless-sdk/hydrogen">Test groups assigned in your loaders, with Hydrogen's analytics and consent.</Card>
  <Card title="Other stacks" href="/headless-sdk/other-stacks">Any server on the Storefront API, the browser only, or without React.</Card>
</CardGroup>

## After setup

Your stack guide covers everything every storefront needs. Do these when the condition applies to yours:

| What you do | When |
| - | - |
| [Render the test price](/headless-sdk/prices) everywhere a price appears, and call `trackExposure` when it is on screen | You run price tests |
| [Replace buttons that skip the cart](/headless-sdk/cart#express-checkout) with a checkout URL or a "Buy now" cart | You have such a button |
| [Render shipping rates and offers](/headless-sdk/prices#show-shipping-rates-and-offers) from the SDK | You show them before checkout |
| [Redirect on your server](/headless-sdk/url-redirects) | You run URL redirect tests, and your storefront is not on Next.js |
| [Render a version per test group](/headless-sdk/feature-tests) in your own code | You run feature tests (coming soon) |
| [Pass `country`](/headless-sdk/prices#prices-by-country) to the client | You sell in more than one country |
| [Pass `customer`](/headless-sdk/targeting#customer) to the client | You target by customer |
| [Pass the visitor's consent choice](/headless-sdk/tracking#consent) to the client | You ask visitors for cookie consent, and your storefront is not on Hydrogen |
| Keep tested pages out of shared caches: [Next.js](/headless-sdk/nextjs-caching), [Hydrogen](/headless-sdk/hydrogen-caching) | You cache HTML at a CDN or in Oxygen's full-page cache |

## Guides

<CardGroup cols={2}>
  <Card title="Prices, shipping, and offers" href="/headless-sdk/prices">Render what the visitor's test group gets.</Card>
  <Card title="Cart and checkout" href="/headless-sdk/cart">Make checkout apply the test, including express checkout.</Card>
  <Card title="URL redirect tests" href="/headless-sdk/url-redirects">Redirect on your server.</Card>
  <Card title="Test groups" href="/headless-sdk/test-groups">Read which test group a visitor is in.</Card>
  <Card title="Feature tests" href="/headless-sdk/feature-tests">Coming soon: test a change you build yourself.</Card>
  <Card title="Targeting" href="/headless-sdk/targeting">How audiences work, and what your storefront supplies.</Card>
  <Card title="Tracking and cookies" href="/headless-sdk/tracking">What the SDK tracks and stores.</Card>
  <Card title="Preview and check" href="/headless-sdk/preview">See one test group and confirm checkout applies it.</Card>
  <Card title="Loading and failures" href="/headless-sdk/loading">What a visitor sees if the SDK cannot load.</Card>
  <Card title="Reference" href="/headless-sdk/reference">Every entry point, method, and option.</Card>
</CardGroup>

## Common mistakes

* **Not writing the cart attributes.** The page shows the test price and checkout charges the original price. The fix: write `getCartAttributes()` on every cart. See [Connect the cart](/headless-sdk/cart).
* **Caching HTML that contains one test group's version.** A full-page cache or a CDN then serves that version to every visitor. The fix: follow your stack's caching page, [Next.js](/headless-sdk/nextjs-caching) or [Hydrogen](/headless-sdk/hydrogen-caching). On other stacks, cache per test group, or leave tested pages uncached.
* **Showing a test price without calling `trackExposure`.** The visitor is counted as assigned but not as having experienced the test. The fix: call it once the price is on screen. See [Record a price exposure](/headless-sdk/tracking#record-a-price-exposure).

Each guide lists the mistakes for its own step.

## FAQ

<Accordion title="Checkout shows the original price">
  The cart is missing ABConvert's attributes. Write `getCartAttributes()` when you create the cart and each time you update it. See [Connect the cart](/headless-sdk/cart).
</Accordion>

<Accordion title="getPriceByVariantId() returns null">
  First check that you passed the product variant ID, not the product ID. Otherwise `null` means no test price applies to this visitor. Render the Storefront API price.
</Accordion>

<Accordion title="The server and the browser show different test groups">
  The browser did not receive the server's state. Pass `getPageState()` from your loader to `ABConvertProvider`, and add `getSetCookieHeaders()` to the loader's response so the visitor ID cookie is set.
</Accordion>

<Accordion title="Do I need to store the visitor's test group myself?">
  No. The SDK keeps the visitor ID and its decisions in cookies and reads them on every request. See [Cookies and storage](/headless-sdk/tracking#cookies-and-storage).
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.