> ## 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 on other stacks

> Set up the ABConvert headless SDK on any server that uses the Storefront API, in the browser only, or without React.

This guide sets up the headless SDK on a storefront that runs on neither [Hydrogen](/headless-sdk/hydrogen) nor [Next.js](/headless-sdk/nextjs): another server framework, a browser-only app, or a stack without React.

Read [Before you start](/headless-sdk/overview#before-you-start) first.

## On your server

Any server that talks to the [Storefront API](https://shopify.dev/docs/api/storefront) can run the SDK. Assigning on the server means the first HTML already shows the right test group.

### Step 1: Install the package

```bash theme={null}
npm install @abconvert/headless
```

### Step 2: Create the client for each request

Pass the incoming request to `createABConvert`, then add `getSetCookieHeaders()` to your response. If your framework gives you no `Request` object, pass its parts.

```ts Server theme={null}
import { createABConvert } from '@abconvert/headless';

const abconvert = await createABConvert({
  shop: 'your-store.myshopify.com',
  storefrontKey: 'abc_pk_4f9c2e7a1b3d5f6e8a0c2b4d6f8e0a1c',
  request, // or { headers, url }
});

// Set the SDK's cookies on the response
for (const cookie of abconvert.getSetCookieHeaders()) {
  response.headers.append('Set-Cookie', cookie);
}
```

The SDK loads your tests once per server and shares them, so creating a client for each request does not call ABConvert each time. Do not cache a response that sets an ABConvert cookie.

### Step 3: Pass the state to the browser

Hand the page's state to the browser so it tracks for the visitor and renders the same test groups the server did. With React, pass `getPageState()` to the provider:

```tsx Browser (provider) theme={null}
import { ABConvertProvider } from '@abconvert/headless/react';

<ABConvertProvider pageState={pageState}>{children}</ABConvertProvider>
```

Without React, pass it to `hydrateABConvert` and call `start()` once the page has loaded:

```ts Browser theme={null}
import { hydrateABConvert } from '@abconvert/headless';

const abconvert = hydrateABConvert(pageState);
abconvert.start();
```

### Step 4: Write the cart attributes

Checkout applies a test only when the cart carries the visitor's test groups. Pass `getCartLineAttributes()` with each line you add, and write `getCartAttributes()` with the cart's own attributes each time you create or update the cart. See [Connect the cart](/headless-sdk/cart).

<Warning>
  This step is required. Without it, checkout charges the original price and the order is not counted in your test.
</Warning>

### Step 5: Track page views and add to cart

Nothing on your stack publishes storefront events, so call `track` for them yourself, in the browser:

```ts Browser theme={null}
// On every page load and client-side route change
abconvert.track('page_viewed');
// On a product page, once its product variant is on screen
abconvert.track('product_viewed', { productId: '8901234567890', variantId: '47522361606401' });
// When a line is added to the cart
abconvert.track('product_added_to_cart', { variantId: '47522361606401', quantity: 1 });
```

If you ask visitors for cookie consent, [tell the SDK the visitor's choice](/headless-sdk/tracking#consent). Then [confirm it works](/headless-sdk/preview#check-the-integration).

## Caching

A page that shows one test group's version must not be served to everyone from a shared cache. Cache tested pages per test group, or leave them uncached. `isPageTested()` on the client made from the request returns `true` when a test can change the page, so you can mark that response `private`.

## Browser only

Use this when you cannot run code on the server. Pass the client options to the provider instead of a state:

```tsx Browser (provider) theme={null}
import { ABConvertProvider } from '@abconvert/headless/react';

<ABConvertProvider options={{ shop: 'your-store.myshopify.com', storefrontKey: 'abc_pk_4f9c2e7a1b3d5f6e8a0c2b4d6f8e0a1c' }}>
  {children}
</ABConvertProvider>
```

The methods are the same. The SDK assigns the visitor after the page loads, so hide prices until `isReady` is `true`. We trade a short delay for a simpler setup.

```tsx Browser (component) theme={null}
const { isReady, getPriceByVariantId } = useABConvert();
if (!isReady) return <PriceSkeleton />;
```

Without React, create the client in the browser with the same options. It resolves once the visitor is assigned:

```ts Browser theme={null}
import { createABConvert } from '@abconvert/headless';

const abconvert = await createABConvert({
  shop: 'your-store.myshopify.com',
  storefrontKey: 'abc_pk_4f9c2e7a1b3d5f6e8a0c2b4d6f8e0a1c',
});
```

Steps 4 and 5 above apply as they are: write the cart attributes and call `track` yourself.

## Without React

`createABConvert` and `hydrateABConvert` do not need React. Use the client directly in Vue, Svelte, or plain JavaScript. The methods are the same as in `useABConvert()`, and `track` works the same. On a server-rendered page, `hydrateABConvert` is the browser side, as in [Step 3](#step-3-pass-the-state-to-the-browser); it also takes the provider's `onAssignment` and `expectCustomerPrivacy` options. See the [reference](/headless-sdk/reference#methods).

## Next steps

* [Render the test price](/headless-sdk/prices) everywhere a price appears.
* [Redirect on your server](/headless-sdk/url-redirects) for URL redirect tests.
* Do the rest of [After setup](/headless-sdk/overview#after-setup).


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