@elevateab/sdk 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +278 -391
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @elevateab/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A/B Testing SDK for Shopify Hydrogen and Next.js stores.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -9,523 +9,410 @@ npm install @elevateab/sdk
|
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
**Peer Dependencies:**
|
|
12
|
+
- `react` >= 18.0.0 or >= 19.0.0
|
|
13
|
+
- `@shopify/hydrogen` >= 2023.10.0 (Hydrogen only)
|
|
14
|
+
- `next` >= 13.0.0 (Next.js only)
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
- `@shopify/hydrogen` >= 2023.10.0 (optional - only for Hydrogen)
|
|
16
|
+
---
|
|
15
17
|
|
|
16
|
-
##
|
|
18
|
+
## Hydrogen Setup
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
| -------------------- | ------- | ------------------------------ |
|
|
20
|
-
| **Shopify Hydrogen** | ✅ Full | Automatic via `useAnalytics()` |
|
|
21
|
-
| **Next.js** | ✅ Full | Manual tracking functions |
|
|
22
|
-
| **Remix** | ✅ Full | Manual tracking functions |
|
|
23
|
-
| **Other React** | ✅ Full | Manual tracking functions |
|
|
20
|
+
Hydrogen uses automatic analytics tracking via Shopify's `useAnalytics()` hook.
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
### 1. Add the Provider
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
import { ElevateProvider } from "@elevateab/sdk";
|
|
24
|
+
```tsx
|
|
25
|
+
// app/root.tsx
|
|
26
|
+
import { ElevateProvider, ElevateAnalytics } from "@elevateab/sdk";
|
|
27
|
+
import { Analytics } from "@shopify/hydrogen";
|
|
33
28
|
|
|
34
29
|
export default function Root() {
|
|
35
30
|
const data = useLoaderData<typeof loader>();
|
|
36
31
|
|
|
37
32
|
return (
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
</
|
|
33
|
+
<Analytics.Provider cart={data.cart} shop={data.shop} consent={data.consent}>
|
|
34
|
+
<ElevateProvider
|
|
35
|
+
storeId="mystore.myshopify.com"
|
|
36
|
+
storefrontAccessToken={env.PUBLIC_STOREFRONT_API_TOKEN}
|
|
37
|
+
>
|
|
38
|
+
<ElevateAnalytics />
|
|
39
|
+
<Outlet />
|
|
40
|
+
</ElevateProvider>
|
|
41
|
+
</Analytics.Provider>
|
|
47
42
|
);
|
|
48
43
|
}
|
|
49
44
|
```
|
|
50
45
|
|
|
51
|
-
|
|
46
|
+
That's it. Analytics events are tracked automatically when users view pages, products, add to cart, etc.
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
- ✅ Sets up Shopify Analytics tracking
|
|
55
|
-
- ✅ Tracks all analytics events with A/B test data
|
|
56
|
-
- ✅ Sends events to CloudFlare Worker
|
|
57
|
-
- ✅ Manages visitor IDs and session tracking
|
|
48
|
+
### 2. Add Anti-Flicker (Recommended)
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
import { useExperiment } from "@elevateab/sdk";
|
|
63
|
-
|
|
64
|
-
function ProductPage() {
|
|
65
|
-
const { variant, isLoading } = useExperiment("test-price-1");
|
|
66
|
-
|
|
67
|
-
if (isLoading) return <div>Loading...</div>;
|
|
50
|
+
Prevents content flash while tests load. Add this script to `<head>` before any other scripts:
|
|
68
51
|
|
|
52
|
+
```tsx
|
|
53
|
+
// app/root.tsx
|
|
54
|
+
export default function Root() {
|
|
69
55
|
return (
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
56
|
+
<html>
|
|
57
|
+
<head>
|
|
58
|
+
<script
|
|
59
|
+
dangerouslySetInnerHTML={{
|
|
60
|
+
__html: `(function(){var d=document.documentElement;d.style.opacity='0';d.style.transition='opacity 0.2s';window.__eabReveal=function(){d.style.opacity='1'};setTimeout(window.__eabReveal,2000)})();`,
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
{/* other head elements */}
|
|
64
|
+
</head>
|
|
65
|
+
<body>{/* ... */}</body>
|
|
66
|
+
</html>
|
|
73
67
|
);
|
|
74
68
|
}
|
|
75
69
|
```
|
|
76
70
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
### Automatic Analytics Tracking
|
|
80
|
-
|
|
81
|
-
The `ElevateProvider` automatically tracks the following Shopify analytics events and enriches them with A/B test data:
|
|
82
|
-
|
|
83
|
-
- `page_viewed` - Page views with test assignments
|
|
84
|
-
- `product_viewed` - Product views
|
|
85
|
-
- `product_added_to_cart` - Add to cart events
|
|
86
|
-
- `product_removed_from_cart` - Remove from cart events
|
|
87
|
-
- `cart_viewed` - Cart page views
|
|
88
|
-
|
|
89
|
-
### Event Data Structure
|
|
90
|
-
|
|
91
|
-
Events are sent to CloudFlare Worker with:
|
|
92
|
-
|
|
93
|
-
- **Test Assignments** (`ab_test_assignments`) - All tests the user is assigned to
|
|
94
|
-
- **Test Views** (`ab_test_views`) - Tests the user has actually viewed
|
|
95
|
-
- **UTM Parameters** - Campaign tracking data
|
|
96
|
-
- **Device/Browser Info** - User agent, device type, OS
|
|
97
|
-
- **Referrer Source** - Traffic source (Google, Facebook, Direct, etc.)
|
|
98
|
-
- **Product/Cart Data** - Product IDs, prices, quantities
|
|
99
|
-
|
|
100
|
-
**No additional setup needed** - just use `ElevateProvider` and everything works automatically!
|
|
71
|
+
Then pass `preventFlickering` to the provider:
|
|
101
72
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Carts are **automatically tagged** with A/B test data for order attribution.
|
|
105
|
-
|
|
106
|
-
### For Hydrogen Stores
|
|
107
|
-
|
|
108
|
-
Cart attributes are automatically updated when `product_added_to_cart` event fires, **if you provide the `storefrontAccessToken`**:
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
73
|
+
```tsx
|
|
111
74
|
<ElevateProvider
|
|
112
75
|
storeId="mystore.myshopify.com"
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
consent={data.consent}
|
|
116
|
-
storefrontAccessToken={env.PUBLIC_STOREFRONT_API_TOKEN} // ← Add this!
|
|
76
|
+
storefrontAccessToken={env.PUBLIC_STOREFRONT_API_TOKEN}
|
|
77
|
+
preventFlickering={true}
|
|
117
78
|
>
|
|
118
|
-
<Outlet />
|
|
119
|
-
</ElevateProvider>
|
|
120
79
|
```
|
|
121
80
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
1. Track the `product_added_to_cart` event
|
|
125
|
-
2. Update cart attributes with A/B test data
|
|
126
|
-
|
|
127
|
-
### For Next.js / Other Stores
|
|
81
|
+
---
|
|
128
82
|
|
|
129
|
-
|
|
83
|
+
## Next.js Setup
|
|
130
84
|
|
|
131
|
-
|
|
132
|
-
await trackAddToCart({
|
|
133
|
-
storeId: "mystore.myshopify.com",
|
|
134
|
-
productId: "123456789",
|
|
135
|
-
variantId: "987654321",
|
|
136
|
-
productPrice: 99.99,
|
|
137
|
-
productQuantity: 1,
|
|
138
|
-
currency: "USD",
|
|
139
|
-
// Add these to auto-update cart attributes:
|
|
140
|
-
cartId: cart.id,
|
|
141
|
-
storefrontAccessToken: process.env.NEXT_PUBLIC_STOREFRONT_TOKEN,
|
|
142
|
-
});
|
|
143
|
-
```
|
|
85
|
+
Next.js requires manual tracking since it doesn't have Shopify's analytics system.
|
|
144
86
|
|
|
145
|
-
###
|
|
87
|
+
### 1. Add the Provider
|
|
146
88
|
|
|
147
|
-
|
|
89
|
+
```tsx
|
|
90
|
+
// app/layout.tsx
|
|
91
|
+
import { ElevateNextProvider } from "@elevateab/sdk/next";
|
|
148
92
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
93
|
+
export default function RootLayout({ children }) {
|
|
94
|
+
return (
|
|
95
|
+
<html>
|
|
96
|
+
<head>
|
|
97
|
+
{/* Anti-flicker script (optional but recommended) */}
|
|
98
|
+
<script
|
|
99
|
+
dangerouslySetInnerHTML={{
|
|
100
|
+
__html: `(function(){var d=document.documentElement;d.style.opacity='0';d.style.transition='opacity 0.2s';window.__eabReveal=function(){d.style.opacity='1'};setTimeout(window.__eabReveal,2000)})();`,
|
|
101
|
+
}}
|
|
102
|
+
/>
|
|
103
|
+
</head>
|
|
104
|
+
<body>
|
|
105
|
+
<ElevateNextProvider
|
|
106
|
+
storeId="mystore.myshopify.com"
|
|
107
|
+
storefrontAccessToken={process.env.NEXT_PUBLIC_STOREFRONT_TOKEN}
|
|
108
|
+
preventFlickering={true}
|
|
109
|
+
>
|
|
110
|
+
{children}
|
|
111
|
+
</ElevateNextProvider>
|
|
112
|
+
</body>
|
|
113
|
+
</html>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
155
116
|
```
|
|
156
117
|
|
|
157
|
-
|
|
158
|
-
|
|
118
|
+
The `ElevateNextProvider` automatically:
|
|
119
|
+
- Tracks page views on route changes
|
|
120
|
+
- Initializes analytics globally
|
|
121
|
+
- Handles anti-flicker reveal
|
|
159
122
|
|
|
160
|
-
###
|
|
123
|
+
### 2. Track Product Views
|
|
161
124
|
|
|
162
|
-
|
|
125
|
+
```tsx
|
|
126
|
+
// app/product/[handle]/page.tsx
|
|
127
|
+
import { ProductViewTracker } from "@elevateab/sdk/next";
|
|
163
128
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
129
|
+
export default function ProductPage({ product }) {
|
|
130
|
+
return (
|
|
131
|
+
<>
|
|
132
|
+
<ProductViewTracker
|
|
133
|
+
productId={product.id}
|
|
134
|
+
productVendor={product.vendor}
|
|
135
|
+
productPrice={parseFloat(product.priceRange.minVariantPrice.amount)}
|
|
136
|
+
currency={product.priceRange.minVariantPrice.currencyCode}
|
|
137
|
+
/>
|
|
138
|
+
{/* Product content */}
|
|
139
|
+
</>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
171
143
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
144
|
+
### 3. Track Add to Cart
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { trackAddToCart } from "@elevateab/sdk";
|
|
148
|
+
|
|
149
|
+
async function handleAddToCart() {
|
|
150
|
+
// Shopify GIDs are automatically converted to numeric IDs
|
|
151
|
+
await trackAddToCart({
|
|
152
|
+
productId: product.id, // "gid://shopify/Product/123" works
|
|
153
|
+
variantId: variant.id, // "gid://shopify/ProductVariant/456" works
|
|
154
|
+
productPrice: 99.99,
|
|
155
|
+
productQuantity: 1,
|
|
156
|
+
currency: "USD",
|
|
157
|
+
cartId: cart.id, // For cart attribute tagging
|
|
158
|
+
});
|
|
159
|
+
}
|
|
180
160
|
```
|
|
181
161
|
|
|
182
|
-
|
|
162
|
+
---
|
|
183
163
|
|
|
184
|
-
|
|
164
|
+
## Using A/B Tests
|
|
185
165
|
|
|
186
|
-
|
|
187
|
-
import { useExperiment } from "@elevateab/sdk";
|
|
166
|
+
### useExperiment Hook
|
|
188
167
|
|
|
189
|
-
|
|
190
|
-
|
|
168
|
+
```tsx
|
|
169
|
+
import { useExperiment } from "@elevateab/sdk";
|
|
191
170
|
|
|
192
|
-
|
|
171
|
+
function PricingSection() {
|
|
172
|
+
const { variant, isLoading } = useExperiment("pricing-test");
|
|
193
173
|
|
|
194
|
-
|
|
195
|
-
if (variant?.isA) return <Price amount={99.99} label="Original" />;
|
|
196
|
-
if (variant?.isB) return <Price amount={89.99} label="Sale Price" />;
|
|
174
|
+
if (isLoading) return <LoadingSkeleton />;
|
|
197
175
|
|
|
198
|
-
|
|
176
|
+
if (variant?.isControl) {
|
|
177
|
+
return <Price amount={99.99} />;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return <Price amount={79.99} />;
|
|
199
181
|
}
|
|
200
182
|
```
|
|
201
183
|
|
|
202
|
-
###
|
|
184
|
+
### Variant Properties
|
|
203
185
|
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
const { variant } = useExperiment("test-headline-1");
|
|
186
|
+
```tsx
|
|
187
|
+
const { variant } = useExperiment("test-id");
|
|
207
188
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
</div>
|
|
216
|
-
);
|
|
217
|
-
}
|
|
189
|
+
variant?.isControl // true if control group
|
|
190
|
+
variant?.isA // true if variant A
|
|
191
|
+
variant?.isB // true if variant B
|
|
192
|
+
variant?.isC // true if variant C
|
|
193
|
+
variant?.isD // true if variant D
|
|
194
|
+
variant?.id // variant ID
|
|
195
|
+
variant?.name // variant name
|
|
218
196
|
```
|
|
219
197
|
|
|
220
|
-
### Multiple
|
|
198
|
+
### Multiple Variants
|
|
221
199
|
|
|
222
|
-
```
|
|
223
|
-
function
|
|
224
|
-
const { variant } = useExperiment("
|
|
200
|
+
```tsx
|
|
201
|
+
function LayoutTest() {
|
|
202
|
+
const { variant } = useExperiment("layout-test");
|
|
225
203
|
|
|
226
204
|
if (variant?.isA) return <LayoutA />;
|
|
227
205
|
if (variant?.isB) return <LayoutB />;
|
|
228
206
|
if (variant?.isC) return <LayoutC />;
|
|
229
|
-
if (variant?.isD) return <LayoutD />;
|
|
230
207
|
|
|
231
|
-
return <
|
|
208
|
+
return <DefaultLayout />;
|
|
232
209
|
}
|
|
233
210
|
```
|
|
234
211
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
### Cookie & Storage Management
|
|
238
|
-
|
|
239
|
-
The SDK uses cookies and session storage for state management:
|
|
212
|
+
---
|
|
240
213
|
|
|
241
|
-
|
|
214
|
+
## Preview Mode
|
|
242
215
|
|
|
243
|
-
|
|
244
|
-
- `ABTL` - Test assignments (which variant each test is assigned to)
|
|
245
|
-
- `ABAU` - Unique test views (which tests have been viewed)
|
|
246
|
-
- `_shopify_y` - Shopify client ID (read-only)
|
|
216
|
+
Test specific variants without affecting live traffic. Add URL parameters:
|
|
247
217
|
|
|
248
|
-
**Session Storage:**
|
|
249
|
-
|
|
250
|
-
- `eabSessionId` - Session identifier
|
|
251
|
-
- `ABAV` - Session test views
|
|
252
|
-
- `eabReferrer` - Entry referrer
|
|
253
|
-
- `eabEntry` - Entry page URL
|
|
254
|
-
|
|
255
|
-
### Utility Functions
|
|
256
|
-
|
|
257
|
-
```typescript
|
|
258
|
-
import {
|
|
259
|
-
assignVariant,
|
|
260
|
-
getTestList,
|
|
261
|
-
getVisitorId,
|
|
262
|
-
getSessionId,
|
|
263
|
-
parseAddViewData,
|
|
264
|
-
} from "@elevateab/sdk";
|
|
265
|
-
|
|
266
|
-
// Get current test assignments
|
|
267
|
-
const assignments = getTestList(); // { "test-1": "variant-a", "test-2": "control" }
|
|
268
|
-
|
|
269
|
-
// Get visitor ID
|
|
270
|
-
const visitorId = getVisitorId(); // "uuid-v4"
|
|
271
|
-
|
|
272
|
-
// Get session ID
|
|
273
|
-
const sessionId = getSessionId(); // "uuid-v4"
|
|
274
|
-
|
|
275
|
-
// Parse analytics data
|
|
276
|
-
const analyticsData = parseAddViewData({
|
|
277
|
-
referrer: document.referrer,
|
|
278
|
-
entryPage: window.location.href,
|
|
279
|
-
userAgent: navigator.userAgent,
|
|
280
|
-
});
|
|
281
|
-
// Returns: UTM params, device type, browser info, referrer source, etc.
|
|
282
218
|
```
|
|
283
|
-
|
|
284
|
-
### Device & Traffic Detection
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
import {
|
|
288
|
-
getDeviceType,
|
|
289
|
-
getTrafficSource,
|
|
290
|
-
checkFacebookBrowser,
|
|
291
|
-
checkInstagramBrowser,
|
|
292
|
-
} from "@elevateab/sdk";
|
|
293
|
-
|
|
294
|
-
const device = getDeviceType(); // "desktop" | "tablet" | "mobile"
|
|
295
|
-
const source = getTrafficSource(); // "facebook" | "google" | "direct" | etc.
|
|
296
|
-
const isFB = checkFacebookBrowser(); // boolean
|
|
297
|
-
const isIG = checkInstagramBrowser(); // boolean
|
|
219
|
+
https://yourstore.com/?eabUserPreview=true&abtid=<test-id>&eab_tests=<short-id>_<variant-id>
|
|
298
220
|
```
|
|
299
221
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
import {
|
|
306
|
-
extractProductId,
|
|
307
|
-
extractProductVariantId,
|
|
308
|
-
cleanCartToken,
|
|
309
|
-
sanitizeString,
|
|
310
|
-
roundToTwo,
|
|
311
|
-
} from "@elevateab/sdk";
|
|
312
|
-
|
|
313
|
-
// Extract IDs from Shopify GIDs
|
|
314
|
-
const productId = extractProductId("gid://shopify/Product/123456");
|
|
315
|
-
// Returns: "123456"
|
|
316
|
-
|
|
317
|
-
const variantId = extractProductVariantId(
|
|
318
|
-
"gid://shopify/ProductVariant/789012"
|
|
319
|
-
);
|
|
320
|
-
// Returns: "789012"
|
|
321
|
-
|
|
322
|
-
// Clean and sanitize data
|
|
323
|
-
const cleanToken = cleanCartToken("token?param=value");
|
|
324
|
-
const safeString = sanitizeString(userInput);
|
|
325
|
-
const price = roundToTwo(99.999); // 100.00
|
|
222
|
+
Example:
|
|
223
|
+
```
|
|
224
|
+
https://yourstore.com/products/shirt?eabUserPreview=true&abtid=abc123&eab_tests=c123_12345
|
|
326
225
|
```
|
|
327
226
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
The SDK provides infrastructure for multiple test types:
|
|
331
|
-
|
|
332
|
-
- **Split URL Tests** - Redirect tests between different URLs
|
|
333
|
-
- **Price Tests** - A/B test different pricing strategies
|
|
334
|
-
- **Content Tests** - Test different copy, headlines, CTAs
|
|
335
|
-
- **Custom Code Tests** - Run custom JavaScript for advanced tests
|
|
336
|
-
|
|
337
|
-
### Example: Split URL Test
|
|
338
|
-
|
|
339
|
-
```typescript
|
|
340
|
-
function SplitURLTest() {
|
|
341
|
-
const { variant } = useExperiment("test-split-url-1");
|
|
227
|
+
Check if in preview mode:
|
|
342
228
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
variant?.handle &&
|
|
346
|
-
window.location.pathname !== `/products/${variant.handle}`
|
|
347
|
-
) {
|
|
348
|
-
window.location.href = `/products/${variant.handle}`;
|
|
349
|
-
}
|
|
350
|
-
}, [variant]);
|
|
229
|
+
```tsx
|
|
230
|
+
import { isPreviewMode } from "@elevateab/sdk";
|
|
351
231
|
|
|
352
|
-
|
|
232
|
+
if (isPreviewMode()) {
|
|
233
|
+
// Show preview indicator
|
|
353
234
|
}
|
|
354
235
|
```
|
|
355
236
|
|
|
356
|
-
|
|
237
|
+
---
|
|
357
238
|
|
|
358
|
-
|
|
359
|
-
function PriceTest({ originalPrice }: { originalPrice: number }) {
|
|
360
|
-
const { variant } = useExperiment("test-price-1");
|
|
239
|
+
## Geo-Targeting
|
|
361
240
|
|
|
362
|
-
|
|
363
|
-
? parseFloat(variant.price)
|
|
364
|
-
: originalPrice;
|
|
241
|
+
Get user's country for location-based tests:
|
|
365
242
|
|
|
366
|
-
|
|
367
|
-
}
|
|
243
|
+
```tsx
|
|
244
|
+
import { getUserCountry } from "@elevateab/sdk";
|
|
245
|
+
|
|
246
|
+
const country = getUserCountry(); // "US", "CA", "GB", etc.
|
|
368
247
|
```
|
|
369
248
|
|
|
370
|
-
|
|
249
|
+
Country is detected from:
|
|
250
|
+
1. `localization` cookie (set by Shopify)
|
|
251
|
+
2. Cloudflare `CF-IPCountry` header
|
|
252
|
+
3. Falls back to `null` if unavailable
|
|
371
253
|
|
|
372
|
-
|
|
254
|
+
---
|
|
373
255
|
|
|
374
|
-
|
|
256
|
+
## Cart Attribute Tagging
|
|
375
257
|
|
|
376
|
-
|
|
258
|
+
Orders are attributed to A/B tests via cart attributes. This happens automatically when you provide `storefrontAccessToken` and `cartId`.
|
|
377
259
|
|
|
378
|
-
|
|
379
|
-
interface ElevateProviderProps {
|
|
380
|
-
storeId: string; // Your Shopify store ID (required)
|
|
381
|
-
cart?: any; // Cart object from Shopify (Hydrogen only)
|
|
382
|
-
shop?: any; // Shop object from Shopify (Hydrogen only)
|
|
383
|
-
consent?: any; // Consent object from Shopify (Hydrogen only)
|
|
384
|
-
hasLocalizedPaths?: boolean; // Set true if homepage is at /en-us/, /fr-ca/, etc.
|
|
385
|
-
workerUrl?: string; // Custom CloudFlare Worker endpoint
|
|
386
|
-
ordersWorkerUrl?: string; // Custom orders endpoint for checkout_completed
|
|
387
|
-
children: React.ReactNode;
|
|
388
|
-
}
|
|
389
|
-
```
|
|
260
|
+
### Automatic (Recommended)
|
|
390
261
|
|
|
391
|
-
|
|
262
|
+
For Hydrogen, pass `storefrontAccessToken` to the provider:
|
|
392
263
|
|
|
393
|
-
```
|
|
264
|
+
```tsx
|
|
394
265
|
<ElevateProvider
|
|
395
266
|
storeId="mystore.myshopify.com"
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
consent={data.consent}
|
|
399
|
-
>
|
|
400
|
-
<Outlet />
|
|
401
|
-
</ElevateProvider>
|
|
267
|
+
storefrontAccessToken={env.PUBLIC_STOREFRONT_API_TOKEN}
|
|
268
|
+
/>
|
|
402
269
|
```
|
|
403
270
|
|
|
404
|
-
|
|
271
|
+
For Next.js, pass `cartId` to `trackAddToCart`:
|
|
405
272
|
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
>
|
|
414
|
-
<Outlet />
|
|
415
|
-
</ElevateProvider>
|
|
273
|
+
```tsx
|
|
274
|
+
trackAddToCart({
|
|
275
|
+
productId: "123",
|
|
276
|
+
variantId: "456",
|
|
277
|
+
cartId: cart.id,
|
|
278
|
+
// ...
|
|
279
|
+
});
|
|
416
280
|
```
|
|
417
281
|
|
|
418
|
-
###
|
|
282
|
+
### Manual (If Needed)
|
|
419
283
|
|
|
420
|
-
|
|
284
|
+
```tsx
|
|
285
|
+
import { updateCartAttributes } from "@elevateab/sdk";
|
|
421
286
|
|
|
422
|
-
|
|
287
|
+
await updateCartAttributes(cartId, {
|
|
288
|
+
storefrontAccessToken: process.env.NEXT_PUBLIC_STOREFRONT_TOKEN,
|
|
289
|
+
});
|
|
290
|
+
```
|
|
423
291
|
|
|
424
|
-
|
|
425
|
-
const { variant, isLoading } = useExperiment("test-id");
|
|
292
|
+
The Storefront Access Token is safe to use client-side. It's a public token designed for browser use.
|
|
426
293
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
// - isA, isB, isC, isD: boolean - Position flags
|
|
433
|
-
// - handle?: string - Product handle (for product tests)
|
|
434
|
-
// - price?: string - Price override (for price tests)
|
|
435
|
-
```
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Utility Functions
|
|
297
|
+
|
|
298
|
+
### Shopify ID Helpers
|
|
436
299
|
|
|
437
|
-
|
|
300
|
+
```tsx
|
|
301
|
+
import { extractShopifyId, isShopifyGid } from "@elevateab/sdk";
|
|
438
302
|
|
|
439
|
-
|
|
303
|
+
// Extract numeric ID from GID
|
|
304
|
+
extractShopifyId("gid://shopify/Product/123456"); // "123456"
|
|
305
|
+
extractShopifyId("123456"); // "123456"
|
|
440
306
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
//
|
|
307
|
+
// Check if string is a GID
|
|
308
|
+
isShopifyGid("gid://shopify/Product/123"); // true
|
|
309
|
+
isShopifyGid("123456"); // false
|
|
444
310
|
```
|
|
445
311
|
|
|
446
|
-
|
|
312
|
+
Note: Tracking functions automatically extract IDs, so you rarely need these directly.
|
|
447
313
|
|
|
448
|
-
|
|
314
|
+
### Visitor & Session
|
|
449
315
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
- `getCartAttributesPayload()` - Get current test data as attributes array
|
|
316
|
+
```tsx
|
|
317
|
+
import { getVisitorId, getSessionId, getTestList } from "@elevateab/sdk";
|
|
453
318
|
|
|
454
|
-
|
|
319
|
+
getVisitorId(); // Persistent visitor UUID
|
|
320
|
+
getSessionId(); // Session UUID
|
|
321
|
+
getTestList(); // { "test-1": "variant-a", "test-2": "control" }
|
|
322
|
+
```
|
|
455
323
|
|
|
456
|
-
|
|
457
|
-
- `getSessionId()` - Get or create session ID
|
|
458
|
-
- `getTestList()` - Get current test assignments
|
|
459
|
-
- `setCookie(name, value)` - Set a cookie (1 year expiration)
|
|
460
|
-
- `getCookie(name)` - Get a cookie value
|
|
324
|
+
### Device & Traffic Detection
|
|
461
325
|
|
|
462
|
-
|
|
326
|
+
```tsx
|
|
327
|
+
import { getDeviceType, getTrafficSource } from "@elevateab/sdk";
|
|
463
328
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
- `extractProductVariantId(gid)` - Extract variant ID from Shopify GID
|
|
329
|
+
getDeviceType(); // "desktop" | "tablet" | "mobile"
|
|
330
|
+
getTrafficSource(); // "facebook" | "google" | "instagram" | "direct" | "other"
|
|
331
|
+
```
|
|
468
332
|
|
|
469
|
-
|
|
333
|
+
---
|
|
470
334
|
|
|
471
|
-
|
|
472
|
-
- ✅ **React Server Components** - Compatible with RSC
|
|
473
|
-
- ✅ **Analytics Tracking** - Automatic event tracking via @shopify/hydrogen
|
|
474
|
-
- ✅ **Cart Tagging** - Attribute orders to A/B tests
|
|
475
|
-
- ✅ **Device Detection** - Mobile, tablet, desktop detection
|
|
476
|
-
- ✅ **Traffic Source Detection** - Facebook, Instagram, Google, etc.
|
|
477
|
-
- ✅ **UTM Parameter Tracking** - Campaign attribution
|
|
478
|
-
- ✅ **ESM & CJS** - Dual format for maximum compatibility
|
|
479
|
-
- ✅ **Tree-shakeable** - Import only what you need
|
|
480
|
-
- ✅ **Minified** - Optimized bundle size
|
|
335
|
+
## Analytics Events
|
|
481
336
|
|
|
482
|
-
|
|
337
|
+
The SDK tracks these events:
|
|
483
338
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
339
|
+
| Event | Hydrogen | Next.js |
|
|
340
|
+
|-------|----------|---------|
|
|
341
|
+
| `page_viewed` | Automatic | Automatic (via provider) |
|
|
342
|
+
| `product_viewed` | Automatic | `ProductViewTracker` or `trackProductView()` |
|
|
343
|
+
| `product_added_to_cart` | Automatic | `trackAddToCart()` |
|
|
344
|
+
| `product_removed_from_cart` | Automatic | `trackRemoveFromCart()` |
|
|
345
|
+
| `cart_viewed` | Automatic | `trackCartView()` |
|
|
346
|
+
| `search_submitted` | Automatic | `trackSearchSubmitted()` |
|
|
347
|
+
| `checkout_started` | Automatic | `trackCheckoutStarted()` |
|
|
348
|
+
| `checkout_completed` | Automatic | `trackCheckoutCompleted()` |
|
|
490
349
|
|
|
491
|
-
|
|
350
|
+
---
|
|
492
351
|
|
|
493
|
-
|
|
494
|
-
- **Next.js**: See [Next.js Example](./NEXTJS_EXAMPLE.md) for manual tracking
|
|
352
|
+
## API Reference
|
|
495
353
|
|
|
496
|
-
|
|
354
|
+
### ElevateProvider Props
|
|
497
355
|
|
|
498
|
-
|
|
356
|
+
```tsx
|
|
357
|
+
interface ElevateProviderProps {
|
|
358
|
+
storeId: string; // Required: your-store.myshopify.com
|
|
359
|
+
storefrontAccessToken?: string; // For cart attribute tagging
|
|
360
|
+
preventFlickering?: boolean; // Enable anti-flicker (default: false)
|
|
361
|
+
hasLocalizedPaths?: boolean; // True if homepage is at /en-us/, /fr-ca/, etc.
|
|
362
|
+
workerUrl?: string; // Custom analytics endpoint
|
|
363
|
+
children: React.ReactNode;
|
|
364
|
+
}
|
|
365
|
+
```
|
|
499
366
|
|
|
500
|
-
###
|
|
367
|
+
### Tracking Functions (Next.js)
|
|
501
368
|
|
|
502
|
-
|
|
369
|
+
All tracking functions accept Shopify GIDs directly - they're converted automatically.
|
|
503
370
|
|
|
504
|
-
|
|
371
|
+
```tsx
|
|
372
|
+
// Page view (auto-tracked by ElevateNextProvider)
|
|
373
|
+
trackPageView();
|
|
505
374
|
|
|
506
|
-
|
|
375
|
+
// Product view
|
|
376
|
+
trackProductView({
|
|
377
|
+
productId: "gid://shopify/Product/123",
|
|
378
|
+
productPrice: 99.99,
|
|
379
|
+
currency: "USD",
|
|
380
|
+
});
|
|
507
381
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
382
|
+
// Add to cart
|
|
383
|
+
trackAddToCart({
|
|
384
|
+
productId: "123",
|
|
385
|
+
variantId: "456",
|
|
386
|
+
productPrice: 99.99,
|
|
387
|
+
productQuantity: 1,
|
|
388
|
+
currency: "USD",
|
|
389
|
+
cartId: "gid://shopify/Cart/abc", // Optional: enables cart tagging
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// Other events
|
|
393
|
+
trackRemoveFromCart({ productId, variantId, ... });
|
|
394
|
+
trackCartView({ cartTotalPrice, cartItems, ... });
|
|
395
|
+
trackSearchSubmitted({ searchQuery });
|
|
396
|
+
trackCheckoutStarted({ cartTotalPrice, cartItems, ... });
|
|
397
|
+
trackCheckoutCompleted({ orderId, cartTotalPrice, cartItems, ... });
|
|
512
398
|
```
|
|
513
399
|
|
|
514
|
-
|
|
515
|
-
- ✅ Complete ElevateProvider integration
|
|
516
|
-
- ✅ Automatic analytics tracking
|
|
517
|
-
- ✅ Cart attribute tagging
|
|
518
|
-
- ✅ Multiple A/B test examples
|
|
519
|
-
- ✅ Real Shopify products and cart
|
|
400
|
+
---
|
|
520
401
|
|
|
521
|
-
|
|
402
|
+
## Cookies & Storage
|
|
522
403
|
|
|
523
|
-
|
|
404
|
+
**Cookies (1 year):**
|
|
405
|
+
- `eabUserId` - Visitor ID
|
|
406
|
+
- `ABTL` - Test assignments
|
|
407
|
+
- `ABAU` - Unique test views
|
|
408
|
+
- `eabUserPreview` - Preview mode flag
|
|
524
409
|
|
|
525
|
-
|
|
410
|
+
**Session Storage:**
|
|
411
|
+
- `eabSessionId` - Session ID
|
|
412
|
+
- `ABAV` - Session test views
|
|
526
413
|
|
|
527
|
-
|
|
414
|
+
---
|
|
528
415
|
|
|
529
|
-
##
|
|
416
|
+
## License
|
|
530
417
|
|
|
531
|
-
|
|
418
|
+
MIT
|