@elevateab/sdk
v1.4.12
Published
Elevate AB Testing SDK for Hydrogen and Remix frameworks
Downloads
872
Readme
@elevateab/sdk
A/B Testing SDK for Shopify Hydrogen and Next.js stores.
Installation
npm install @elevateab/sdkPeer Dependencies:
react>= 18.0.0 or >= 19.0.0@shopify/hydrogen>= 2023.10.0 (Hydrogen only)next>= 13.0.0 (Next.js only)
Hydrogen Setup
Hydrogen uses automatic analytics tracking via Shopify's useAnalytics() hook.
1. Content Security Policy (CSP) Setup (first)
If your store uses a strict CSP, allow Elevate domains before wiring the provider.
// app/entry.server.tsx
import { createContentSecurityPolicy } from "@shopify/hydrogen";
const { nonce, header, NonceProvider } = createContentSecurityPolicy({
shop: {
checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
storeDomain: context.env.PUBLIC_STORE_DOMAIN,
},
scriptSrc: [
"'self'",
"https://cdn.shopify.com",
"https://ds0wlyksfn0sb.cloudfront.net",
],
connectSrc: [
"https://ds0wlyksfn0sb.cloudfront.net",
"https://bitter-river-9c62.support-67d.workers.dev",
"https://d339co84ntxcme.cloudfront.net",
"https://configs.elevateab.com",
],
});2. Add Elevate Provider + Hydrogen Analytics
// app/root.tsx
import { Analytics, useNonce } from "@shopify/hydrogen";
import { ElevateProvider } from "@elevateab/sdk";
import { ElevateHydrogenAnalytics } from "@elevateab/sdk/hydrogen";
import { Outlet, useLoaderData } from "@remix-run/react";
export default function Root() {
const data = useLoaderData<typeof loader>();
const nonce = useNonce();
return (
<Analytics.Provider cart={data.cart} shop={data.shop} consent={data.consent}>
{/* ElevateProvider must be inside Analytics.Provider */}
<ElevateProvider
storeId="mystore.myshopify.com"
storefrontAccessToken={data.consent?.storefrontAccessToken}
preventFlickering={true}
nonce={nonce}
>
{/* ElevateHydrogenAnalytics must be inside ElevateProvider */}
<ElevateHydrogenAnalytics />
<Outlet />
</ElevateProvider>
</Analytics.Provider>
);
}That's it. Analytics events are tracked automatically when users view pages, products, add to cart, etc.
The preventFlickering={true} prop prevents content flash while tests load.
Next.js Setup
Next.js requires manual tracking since it doesn't have Shopify's analytics system.
1. Add the Provider
// app/layout.tsx
import { ElevateNextProvider } from "@elevateab/sdk/next";
export default function RootLayout({ children }) {
return (
<html>
<body>
<ElevateNextProvider
storeId="mystore.myshopify.com"
storefrontAccessToken={process.env.NEXT_PUBLIC_STOREFRONT_TOKEN}
preventFlickering={true}
>
{children}
</ElevateNextProvider>
</body>
</html>
);
}The ElevateNextProvider automatically:
- Tracks page views on route changes
- Initializes analytics globally
- Prevents content flicker (when
preventFlickering={true})
2. Track Product Views
// app/product/[handle]/page.tsx
import { ProductViewTracker } from "@elevateab/sdk/next";
export default function ProductPage({ product }) {
return (
<>
<ProductViewTracker
productId={product.id}
productVendor={product.vendor}
productPrice={parseFloat(product.priceRange.minVariantPrice.amount)}
currency={product.priceRange.minVariantPrice.currencyCode}
/>
{/* Product content */}
</>
);
}3. Track Add to Cart
import { trackAddToCart } from "@elevateab/sdk";
async function handleAddToCart() {
// Shopify GIDs are automatically converted to numeric IDs
await trackAddToCart({
productId: product.id, // "gid://shopify/Product/123" works
variantId: variant.id, // "gid://shopify/ProductVariant/456" works
productPrice: 99.99,
productQuantity: 1,
currency: "USD",
cartId: cart.id, // For cart attribute tagging
});
}4. Other Tracking Events
import {
trackRemoveFromCart,
trackCartView,
trackSearchSubmitted,
trackCheckoutStarted,
trackCheckoutCompleted,
} from "@elevateab/sdk";
// Remove from cart
await trackRemoveFromCart({
productId: product.id,
variantId: variant.id,
productPrice: 99.99,
productQuantity: 1,
});
// Cart view
await trackCartView({
cartTotalPrice: 199.99,
cartTotalQuantity: 2,
currency: "USD",
cartItems: [
{
productId: "123",
variantId: "456",
productPrice: 99.99,
productQuantity: 1,
},
],
});
// Search
await trackSearchSubmitted({ searchQuery: "blue shirt" });Using A/B Tests
useExperiment Hook (optional)
Note:
useExperimentis only needed if you want to write conditional logic in your components based on which variant a user is in. Most test types (content, custom code, split URL, price) run automatically — you don't need this hook for those.
import { useExperiment } from "@elevateab/sdk";
function PricingSection() {
const { variant, isLoading } = useExperiment("pricing-test");
if (isLoading) return <LoadingSkeleton />;
if (variant?.isControl) {
return <Price amount={99.99} />;
}
return <Price amount={79.99} />;
}Variant Properties
const { variant } = useExperiment("test-id");
variant?.isControl; // true if control group
variant?.isA; // true if variant A
variant?.isB; // true if variant B
variant?.isC; // true if variant C
variant?.isD; // true if variant D
variant?.id; // variant ID
variant?.name; // variant nameMultiple Variants
function LayoutTest() {
const { variant } = useExperiment("layout-test");
if (variant?.isA) return <LayoutA />;
if (variant?.isB) return <LayoutB />;
if (variant?.isC) return <LayoutC />;
return <DefaultLayout />;
}Preview Mode
Test specific variants without affecting live traffic. Add URL parameters:
https://yourstore.com/?eabUserPreview=true&abtid=<test-id>&eab_tests=<short-id>_<variant-id>Example:
https://yourstore.com/products/shirt?eabUserPreview=true&abtid=abc123&eab_tests=c123_12345Check if in preview mode:
import { isPreviewMode } from "@elevateab/sdk";
if (isPreviewMode()) {
// Show preview indicator
}Cart Attribute Tagging
Orders are attributed to A/B tests via cart attributes. This happens automatically when you provide storefrontAccessToken and cartId.
For Hydrogen, pass storefrontAccessToken to the provider:
<ElevateProvider
storeId="mystore.myshopify.com"
storefrontAccessToken={env.PUBLIC_STOREFRONT_API_TOKEN}
/>For Next.js, pass cartId to trackAddToCart:
trackAddToCart({
productId: "123",
variantId: "456",
cartId: cart.id,
// ...
});The Storefront Access Token is safe to use client-side - it's a public token designed for browser use.
Utility Functions
Shopify ID Helpers
import { extractShopifyId, isShopifyGid } from "@elevateab/sdk";
extractShopifyId("gid://shopify/Product/123456"); // "123456"
isShopifyGid("gid://shopify/Product/123"); // trueNote: Tracking functions automatically extract IDs, so you rarely need these directly.
API Reference
ElevateProvider Props
interface ElevateProviderProps {
storeId: string; // Required: your-store.myshopify.com
storefrontAccessToken?: string; // For cart attribute tagging
preventFlickering?: boolean; // Enable anti-flicker (default: false)
flickerTimeout?: number; // Max wait time in ms (default: 3000)
children: React.ReactNode;
}Analytics Events Summary
| Event | Hydrogen | Next.js |
| ------------------ | --------- | -------------------------- |
| Page view | Automatic | Automatic |
| Product view | Automatic | ProductViewTracker |
| Add to cart | Automatic | trackAddToCart() |
| Remove from cart | Automatic | trackRemoveFromCart() |
| Cart view | Automatic | trackCartView() |
| Search | Automatic | trackSearchSubmitted() |
| Checkout started | Automatic | trackCheckoutStarted() |
| Checkout completed | Automatic | trackCheckoutCompleted() |
License
MIT
