@getcontextual/shopify-sdk 1.1.0
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/LICENSE +204 -0
- package/README.md +652 -0
- package/THIRD_PARTY +43 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6260 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +6246 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/react/index.d.ts +49 -0
- package/dist/react/tracker.d.ts +71 -0
- package/dist/react/tracking-proxy.d.ts +5 -0
- package/dist/react/types.d.ts +12 -0
- package/dist/react/vanilla/index.d.ts +40 -0
- package/dist/react/vanilla/index.e2e.test.d.ts +9 -0
- package/dist/react/vanilla/index.test.d.ts +1 -0
- package/dist/tracker.d.ts +71 -0
- package/dist/tracking-proxy.d.ts +5 -0
- package/dist/types.d.ts +12 -0
- package/dist/vanilla/index.d.ts +7 -0
- package/dist/vanilla/index.e2e.test.d.ts +9 -0
- package/dist/vanilla/index.js +4787 -0
- package/dist/vanilla/index.js.map +1 -0
- package/dist/vanilla/index.test.d.ts +1 -0
- package/dist/vanilla/react/index.d.ts +49 -0
- package/dist/vanilla/tracker.d.ts +71 -0
- package/dist/vanilla/tracking-proxy.d.ts +5 -0
- package/dist/vanilla/types.d.ts +12 -0
- package/dist/vanilla/vanilla/index.d.ts +40 -0
- package/dist/vanilla/vanilla/index.e2e.test.d.ts +9 -0
- package/dist/vanilla/vanilla/index.test.d.ts +1 -0
- package/package.json +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
# @getcontextual/shopify-sdk
|
|
2
|
+
|
|
3
|
+
A comprehensive tracking SDK for headless Shopify stores. Works with vanilla JavaScript, React, and any SSR framework. Features SSR-safe tracking, and automatic GTM integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Framework Agnostic**: Works with vanilla JS, React, and SSR frameworks like Next.js
|
|
8
|
+
- 🔒 **SSR-Safe**: All tracking methods gracefully handle server-side rendering
|
|
9
|
+
- 📦 **Automatic GTM**: Zero-configuration Google Tag Manager integration
|
|
10
|
+
- 📊 **Comprehensive Events**: Product views, collections, cart, checkout, search, and more
|
|
11
|
+
- 🎨 **TypeScript**: Full TypeScript support with Shopify type definitions
|
|
12
|
+
- âš¡ **Auto-Flush**: Automatic event batching for optimal performance
|
|
13
|
+
- 🔄 **React Safe**: Handles React Strict Mode and SSR correctly
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @getcontextual/shopify-sdk
|
|
19
|
+
# or
|
|
20
|
+
pnpm add @getcontextual/shopify-sdk
|
|
21
|
+
# or
|
|
22
|
+
yarn add @getcontextual/shopify-sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Peer Dependencies:** React (≥16.8.0) is optional and only needed if using React components.
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### Vanilla JavaScript
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
import { init, trackProductView } from "@getcontextual/shopify-sdk/vanilla";
|
|
33
|
+
|
|
34
|
+
// Initialize once
|
|
35
|
+
init({
|
|
36
|
+
merchantId: "your-store.myshopify.com",
|
|
37
|
+
autoTrackPageView: true,
|
|
38
|
+
gtmContainerId: "GTM-XXXXXXX", // Optional
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Track events
|
|
42
|
+
trackProductView(productVariant);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### React
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { TrackerProvider, useTracker } from "@getcontextual/shopify-sdk/react";
|
|
49
|
+
|
|
50
|
+
function App() {
|
|
51
|
+
return (
|
|
52
|
+
<TrackerProvider config={{ merchantId: "your-store.myshopify.com" }}>
|
|
53
|
+
<ProductPage product={product} />
|
|
54
|
+
</TrackerProvider>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function ProductPage({ product }) {
|
|
59
|
+
const { trackProductView } = useTracker();
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
trackProductView(product.variant);
|
|
63
|
+
}, [product.variant, trackProductView]);
|
|
64
|
+
|
|
65
|
+
return <div>{product.title}</div>;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Vanilla JavaScript API
|
|
70
|
+
|
|
71
|
+
Perfect for static sites, Vue.js, Svelte, or any framework without React.
|
|
72
|
+
|
|
73
|
+
### Initialization
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
import { init } from "@getcontextual/shopify-sdk/vanilla";
|
|
77
|
+
|
|
78
|
+
init({
|
|
79
|
+
merchantId: "your-store.myshopify.com", // Required
|
|
80
|
+
autoTrackPageView: true, // Automatically track page views
|
|
81
|
+
gtmContainerId: "GTM-XXXXXXX", // Optional: GTM container ID
|
|
82
|
+
autoFlush: true, // Auto-flush events (default: true)
|
|
83
|
+
flushInterval: 100, // Flush interval in ms (default: 100)
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Tracking Events
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
import {
|
|
91
|
+
trackPageView,
|
|
92
|
+
trackProductView,
|
|
93
|
+
trackCollectionView,
|
|
94
|
+
trackAddToCart,
|
|
95
|
+
trackCheckoutStarted,
|
|
96
|
+
trackCheckoutCompleted,
|
|
97
|
+
trackSearch,
|
|
98
|
+
identify,
|
|
99
|
+
} from "@getcontextual/shopify-sdk/vanilla";
|
|
100
|
+
|
|
101
|
+
// Track page view
|
|
102
|
+
trackPageView();
|
|
103
|
+
|
|
104
|
+
// Track product view
|
|
105
|
+
trackProductView(productVariant);
|
|
106
|
+
|
|
107
|
+
// Track collection view
|
|
108
|
+
trackCollectionView(collection);
|
|
109
|
+
|
|
110
|
+
// Track add to cart
|
|
111
|
+
trackAddToCart(cartLine);
|
|
112
|
+
// Track checkout
|
|
113
|
+
trackCheckoutStarted(checkout);
|
|
114
|
+
trackCheckoutCompleted(checkout);
|
|
115
|
+
|
|
116
|
+
// Track search
|
|
117
|
+
trackSearch(query, searchResults);
|
|
118
|
+
|
|
119
|
+
// Identify user
|
|
120
|
+
identify({
|
|
121
|
+
email: "user@example.com",
|
|
122
|
+
phone: "+1234567890",
|
|
123
|
+
firstName: "John",
|
|
124
|
+
lastName: "Doe",
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## React API
|
|
129
|
+
|
|
130
|
+
Uses React Context for dependency injection, making it easy to access tracking functions throughout your app.
|
|
131
|
+
|
|
132
|
+
### Setup
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { TrackerProvider } from "@getcontextual/shopify-sdk/react";
|
|
136
|
+
|
|
137
|
+
function App() {
|
|
138
|
+
return (
|
|
139
|
+
<TrackerProvider
|
|
140
|
+
config={{
|
|
141
|
+
merchantId: "your-store.myshopify.com",
|
|
142
|
+
gtmContainerId: "GTM-XXXXXXX", // Optional
|
|
143
|
+
autoFlush: true,
|
|
144
|
+
flushInterval: 100,
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
<YourApp />
|
|
148
|
+
</TrackerProvider>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Using Hooks
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import { useTracker } from "@getcontextual/shopify-sdk/react";
|
|
157
|
+
|
|
158
|
+
function ProductPage({ product }) {
|
|
159
|
+
const { trackProductView } = useTracker();
|
|
160
|
+
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
trackProductView(product.variant);
|
|
163
|
+
}, [product.variant, trackProductView]);
|
|
164
|
+
|
|
165
|
+
return <div>{product.title}</div>;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function AddToCartButton({ cartLine }) {
|
|
169
|
+
const { trackAddToCart } = useTracker();
|
|
170
|
+
|
|
171
|
+
const handleClick = () => {
|
|
172
|
+
// Your add to cart logic
|
|
173
|
+
trackAddToCart(cartLine);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return <button onClick={handleClick}>Add to Cart</button>;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Using Components
|
|
181
|
+
|
|
182
|
+
For automatic tracking when components mount:
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
import {
|
|
186
|
+
ProductTracking,
|
|
187
|
+
CollectionTracking,
|
|
188
|
+
AddToCartTracking,
|
|
189
|
+
CheckoutStartedTracking,
|
|
190
|
+
CheckoutCompletedTracking,
|
|
191
|
+
IdentifyTracking,
|
|
192
|
+
PageViewTracker,
|
|
193
|
+
} from "@getcontextual/shopify-sdk/react";
|
|
194
|
+
|
|
195
|
+
function ProductPage({ product }) {
|
|
196
|
+
return (
|
|
197
|
+
<>
|
|
198
|
+
<ProductTracking productVariant={product.variant} />
|
|
199
|
+
<h1>{product.title}</h1>
|
|
200
|
+
</>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function CheckoutPage({ checkout }) {
|
|
205
|
+
return (
|
|
206
|
+
<>
|
|
207
|
+
<CheckoutStartedTracking checkout={checkout} />
|
|
208
|
+
{/* Your checkout UI */}
|
|
209
|
+
</>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Nextjs/Server-Side Rendering (SSR) Guide
|
|
215
|
+
|
|
216
|
+
All tracking methods are SSR-safe and will gracefully no-op on the server-side:
|
|
217
|
+
|
|
218
|
+
- ✅ Safe to call tracking methods during SSR
|
|
219
|
+
- ✅ No errors or warnings on the server
|
|
220
|
+
- ✅ Events only tracked on the client-side after hydration
|
|
221
|
+
- ✅ Works with Next.js, Remix, and other SSR frameworks
|
|
222
|
+
|
|
223
|
+
### Initialize Tracker
|
|
224
|
+
|
|
225
|
+
Create a client component to initialize the tracker:
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
"use client";
|
|
229
|
+
|
|
230
|
+
import { useEffect, useContext, createContext } from "react";
|
|
231
|
+
import {
|
|
232
|
+
init,
|
|
233
|
+
trackPageView,
|
|
234
|
+
trackProductView,
|
|
235
|
+
trackCollectionView,
|
|
236
|
+
trackAddToCart,
|
|
237
|
+
trackCheckoutStarted,
|
|
238
|
+
trackCheckoutCompleted,
|
|
239
|
+
trackSearch,
|
|
240
|
+
identify,
|
|
241
|
+
} from "@getcontextual/shopify-sdk/vanilla";
|
|
242
|
+
|
|
243
|
+
import type { HeadlessShopifyConfig } from "../tracker";
|
|
244
|
+
|
|
245
|
+
interface TrackerContextValue {
|
|
246
|
+
trackPageView: typeof trackPageView;
|
|
247
|
+
trackProductView: typeof trackProductView;
|
|
248
|
+
trackCollectionView: typeof trackCollectionView;
|
|
249
|
+
trackAddToCart: typeof trackAddToCart;
|
|
250
|
+
trackCheckoutStarted: typeof trackCheckoutStarted;
|
|
251
|
+
trackCheckoutCompleted: typeof trackCheckoutCompleted;
|
|
252
|
+
trackSearch: typeof trackSearch;
|
|
253
|
+
identify: typeof identify;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const TrackerContext = createContext<TrackerContextValue | null>(null);
|
|
257
|
+
|
|
258
|
+
export function TrackerProvider({
|
|
259
|
+
merchantId: string,
|
|
260
|
+
}: {
|
|
261
|
+
merchantId: string;
|
|
262
|
+
}) {
|
|
263
|
+
useEffect(() => {
|
|
264
|
+
init({ merchantId, autoTrackPageView: false });
|
|
265
|
+
}, [config]);
|
|
266
|
+
|
|
267
|
+
const value: TrackerContextValue = {
|
|
268
|
+
trackPageView,
|
|
269
|
+
trackProductView,
|
|
270
|
+
trackCollectionView,
|
|
271
|
+
trackAddToCart,
|
|
272
|
+
trackSearch,
|
|
273
|
+
trackCheckoutStarted,
|
|
274
|
+
trackCheckoutCompleted,
|
|
275
|
+
identify,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<TrackerContext.Provider value={value}>{children}</TrackerContext.Provider>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function useTracker() {
|
|
284
|
+
const context = useContext(TrackerContext);
|
|
285
|
+
if (!context) {
|
|
286
|
+
throw new Error("useTracker must be used within a TrackerProvider");
|
|
287
|
+
}
|
|
288
|
+
return context;
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Automatic Page View Tracker
|
|
293
|
+
|
|
294
|
+
#### With Page Router
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
// components/page-view-tracker.tsx
|
|
298
|
+
"use client";
|
|
299
|
+
|
|
300
|
+
import { useEffect } from "react";
|
|
301
|
+
import { useRouter } from "next/router";
|
|
302
|
+
// contextual sdk is global singleton so can be accessed like this
|
|
303
|
+
import { trackPageView } from "@getcontextual/shopify-sdk/vanilla";
|
|
304
|
+
|
|
305
|
+
export function PageViewTracker() {
|
|
306
|
+
const router = useRouter();
|
|
307
|
+
|
|
308
|
+
useEffect(() => {
|
|
309
|
+
const handleRouteChange = () => {
|
|
310
|
+
void trackPageView();
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
router.events.on("routeChangeComplete", handleRouteChange);
|
|
314
|
+
|
|
315
|
+
return () => {
|
|
316
|
+
router.events.off("routeChangeComplete", handleRouteChange);
|
|
317
|
+
};
|
|
318
|
+
}, [router.events]);
|
|
319
|
+
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### With App Router
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
"use client";
|
|
328
|
+
// contextual sdk is global singleton so can be accessed like this
|
|
329
|
+
import { useEffect, Suspense } from "react";
|
|
330
|
+
import { usePathname, useSearchParams } from "next/navigation";
|
|
331
|
+
import { useTracker } from "./tracker-provider";
|
|
332
|
+
|
|
333
|
+
function PageViewHandler() {
|
|
334
|
+
const pathname = usePathname();
|
|
335
|
+
const searchParams = useSearchParams();
|
|
336
|
+
const { trackPageView } = useTracker();
|
|
337
|
+
|
|
338
|
+
useEffect(() => {
|
|
339
|
+
trackPageView();
|
|
340
|
+
}, [pathname, searchParams, trackPageView]);
|
|
341
|
+
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function AppRouterPageViewTracker() {
|
|
346
|
+
return (
|
|
347
|
+
<Suspense fallback={null}>
|
|
348
|
+
<PageViewHandler />
|
|
349
|
+
</Suspense>
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
```tsx
|
|
355
|
+
// app/layout.tsx
|
|
356
|
+
|
|
357
|
+
export default function RootLayout({ children }) {
|
|
358
|
+
return (
|
|
359
|
+
<html>
|
|
360
|
+
<body>
|
|
361
|
+
<TrackerProvider config={{ merchantId: "my-store.myshopify.com" }}>
|
|
362
|
+
<AppRouterPageViewTracker />
|
|
363
|
+
{children}
|
|
364
|
+
</TrackerProvider>
|
|
365
|
+
</body>
|
|
366
|
+
</html>
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Using Components
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
"use client";
|
|
375
|
+
|
|
376
|
+
import { trackProductView } from "@getcontextual/shopify-sdk/vanilla";
|
|
377
|
+
|
|
378
|
+
export function ProductTracking({
|
|
379
|
+
productVariant,
|
|
380
|
+
}: {
|
|
381
|
+
productVariant: ShopifyProductVariant;
|
|
382
|
+
}) {
|
|
383
|
+
const hasTracked = useRef(false);
|
|
384
|
+
|
|
385
|
+
useEffect(() => {
|
|
386
|
+
if (!hasTracked.current && productVariant) {
|
|
387
|
+
hasTracked.current = true;
|
|
388
|
+
void trackProductView(productVariant);
|
|
389
|
+
}
|
|
390
|
+
}, [productVariant]);
|
|
391
|
+
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Using Hooks
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
// pages/products/[id].tsx
|
|
400
|
+
|
|
401
|
+
import { useEffect } from "react";
|
|
402
|
+
import { trackProductView } from "@getcontextual/shopify-sdk/vanilla";
|
|
403
|
+
|
|
404
|
+
export default function ProductPage({ product }) {
|
|
405
|
+
useEffect(() => {
|
|
406
|
+
if (product?.variant) {
|
|
407
|
+
void trackProductView(product.variant);
|
|
408
|
+
}
|
|
409
|
+
}, [product]);
|
|
410
|
+
|
|
411
|
+
return <div>{product.title}</div>;
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
##W Key Points
|
|
416
|
+
|
|
417
|
+
1. **Always use `'use client'`**: Any file that imports from `@getcontextual/shopify-sdk` must have `'use client'` at the top
|
|
418
|
+
2. **SSR-safe**: All tracking functions are SSR-safe and will no-op on the server
|
|
419
|
+
3. **Initialize once**: Call `init()` only once, typically in a provider component
|
|
420
|
+
4. **Track in useEffect**: Always track events inside `useEffect` to ensure they run on the client-side
|
|
421
|
+
5. **Page views**: Track page views when route changes (App Router: `usePathname`/`useSearchParams`, Pages Router: `router.events`)
|
|
422
|
+
|
|
423
|
+
## Configuration
|
|
424
|
+
|
|
425
|
+
### HeadlessShopifyConfig
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
interface HeadlessShopifyConfig {
|
|
429
|
+
merchantId: string; // Required: Your Shopify store domain (e.g., 'store.myshopify.com')
|
|
430
|
+
version?: string; // Optional: SDK version (default: '1.0.0')
|
|
431
|
+
autoFlush?: boolean; // Optional: Auto-flush events (default: true)
|
|
432
|
+
flushInterval?: number; // Optional: Flush interval in milliseconds (default: 100)
|
|
433
|
+
gtmContainerId?: string; // Optional: Google Tag Manager container ID
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### VanillaShopifyConfig
|
|
438
|
+
|
|
439
|
+
Extends `HeadlessShopifyConfig` with:
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
interface VanillaShopifyConfig extends HeadlessShopifyConfig {
|
|
443
|
+
autoTrackPageView: boolean; // Automatically track page views on init
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## TypeScript Support
|
|
448
|
+
|
|
449
|
+
The package is fully typed. All Shopify types are based on `@shopify/web-pixels-extension` for consistency.
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
import type {
|
|
453
|
+
ShopifyProductVariant,
|
|
454
|
+
ShopifyCartLine,
|
|
455
|
+
ShopifyCheckout,
|
|
456
|
+
ShopifyCollection,
|
|
457
|
+
ShopifySearchResult,
|
|
458
|
+
HeadlessShopifyConfig,
|
|
459
|
+
} from "@getcontextual/shopify-sdk";
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Google Tag Manager Integration
|
|
463
|
+
|
|
464
|
+
### Setup
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
init({
|
|
468
|
+
merchantId: "your-store.myshopify.com",
|
|
469
|
+
gtmContainerId: "GTM-XXXXXXX", // Your GTM container ID
|
|
470
|
+
});
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### How It Works
|
|
474
|
+
|
|
475
|
+
1. **Automatic Loading**: GTM script loads automatically on tracker initialization
|
|
476
|
+
2. **Event Pushing**: All events are pushed to `window.dataLayer`
|
|
477
|
+
3. **Event Format**: Events follow standard GTM data layer format
|
|
478
|
+
|
|
479
|
+
### Event Structure
|
|
480
|
+
|
|
481
|
+
Events are pushed to `dataLayer` as:
|
|
482
|
+
|
|
483
|
+
```javascript
|
|
484
|
+
{
|
|
485
|
+
event: 'product_viewed',
|
|
486
|
+
event_data: {
|
|
487
|
+
merchant_id: 'your-store.myshopify.com',
|
|
488
|
+
event_id: 'unique-event-id',
|
|
489
|
+
// ... product data, user data, etc.
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
**Note:** If you don't provide a `gtmContainerId`, the SDK works perfectly without GTM. Events will only be sent to the Contextual backend.
|
|
495
|
+
|
|
496
|
+
No additional configuration required - it works out of the box!
|
|
497
|
+
|
|
498
|
+
## API Reference
|
|
499
|
+
|
|
500
|
+
### Vanilla JavaScript Functions
|
|
501
|
+
|
|
502
|
+
All functions from `@getcontextual/shopify-sdk/vanilla`:
|
|
503
|
+
|
|
504
|
+
- `init(config: VanillaShopifyConfig)` - Initialize tracker
|
|
505
|
+
- `getTracker()` - Get tracker instance
|
|
506
|
+
- `trackPageView()` - Track page view
|
|
507
|
+
- `trackProductView(productVariant)` - Track product view
|
|
508
|
+
- `trackCollectionView(collection)` - Track collection view
|
|
509
|
+
- `trackAddToCart(cartLine)` - Track add to cart
|
|
510
|
+
- `trackCheckoutStarted(checkout)` - Track checkout started
|
|
511
|
+
- `trackCheckoutCompleted(checkout)` - Track checkout completed
|
|
512
|
+
- `trackSearch(query, results)` - Track search
|
|
513
|
+
- `identify(userProperties)` - Identify user
|
|
514
|
+
|
|
515
|
+
### React Hooks
|
|
516
|
+
|
|
517
|
+
From `@getcontextual/shopify-sdk/react`:
|
|
518
|
+
|
|
519
|
+
- `useTracker()` - Access all tracker functions from context
|
|
520
|
+
|
|
521
|
+
### React Components
|
|
522
|
+
|
|
523
|
+
From `@getcontextual/shopify-sdk/react`:
|
|
524
|
+
|
|
525
|
+
- `<TrackerProvider config={config}>` - Provider component
|
|
526
|
+
- `<ProductTracking productVariant={variant} />` - Track product views
|
|
527
|
+
- `<CollectionTracking collection={collection} />` - Track collection views
|
|
528
|
+
- `<AddToCartTracking cartLine={cartLine} />` - Track add to cart
|
|
529
|
+
- `<CheckoutStartedTracking checkout={checkout} />` - Track checkout started
|
|
530
|
+
- `<CheckoutCompletedTracking checkout={checkout} />` - Track checkout completed
|
|
531
|
+
- `<IdentifyTracking userProperties={props} />` - Identify user
|
|
532
|
+
- `<PageViewTracker pathname={pathname} />` - Track page views
|
|
533
|
+
- `<SearchTracking search={search}, searchResult={searchResult}>` - Track search results
|
|
534
|
+
|
|
535
|
+
## Examples
|
|
536
|
+
|
|
537
|
+
### Complete Vanilla JS Example
|
|
538
|
+
|
|
539
|
+
```javascript
|
|
540
|
+
import {
|
|
541
|
+
init,
|
|
542
|
+
trackProductView,
|
|
543
|
+
trackAddToCart,
|
|
544
|
+
identify,
|
|
545
|
+
} from "@getcontextual/shopify-sdk/vanilla";
|
|
546
|
+
|
|
547
|
+
// Initialize
|
|
548
|
+
init({
|
|
549
|
+
merchantId: "my-store.myshopify.com",
|
|
550
|
+
autoTrackPageView: true,
|
|
551
|
+
gtmContainerId: "GTM-XXXXXXX",
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// Product page
|
|
555
|
+
function onProductPageLoad(product) {
|
|
556
|
+
trackProductView(product.variant);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Add to cart button
|
|
560
|
+
function onAddToCart(cartLine) {
|
|
561
|
+
trackAddToCart(cartLine);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// User login
|
|
565
|
+
function onUserLogin(user) {
|
|
566
|
+
identify({
|
|
567
|
+
email: user.email,
|
|
568
|
+
firstName: user.firstName,
|
|
569
|
+
lastName: user.lastName,
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Complete React Example
|
|
575
|
+
|
|
576
|
+
```tsx
|
|
577
|
+
import {
|
|
578
|
+
TrackerProvider,
|
|
579
|
+
useTracker,
|
|
580
|
+
ProductTracking,
|
|
581
|
+
} from "@getcontextual/shopify-sdk/react";
|
|
582
|
+
|
|
583
|
+
function App() {
|
|
584
|
+
return (
|
|
585
|
+
<TrackerProvider config={{ merchantId: "my-store.myshopify.com" }}>
|
|
586
|
+
<Router>
|
|
587
|
+
<Routes>
|
|
588
|
+
<Route path="/products/:id" element={<ProductPage />} />
|
|
589
|
+
<Route path="/cart" element={<CartPage />} />
|
|
590
|
+
</Routes>
|
|
591
|
+
</Router>
|
|
592
|
+
</TrackerProvider>
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function ProductPage() {
|
|
597
|
+
const { product } = useLoaderData();
|
|
598
|
+
const { trackAddToCart } = useTracker();
|
|
599
|
+
|
|
600
|
+
return (
|
|
601
|
+
<>
|
|
602
|
+
<ProductTracking productVariant={product.variant} />
|
|
603
|
+
<div>
|
|
604
|
+
<h1>{product.title}</h1>
|
|
605
|
+
<button onClick={() => trackAddToCart(product.cartLine)}>
|
|
606
|
+
Add to Cart
|
|
607
|
+
</button>
|
|
608
|
+
</div>
|
|
609
|
+
</>
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
## Package Exports
|
|
615
|
+
|
|
616
|
+
- **`@getcontextual/shopify-sdk`** - Core tracker class and types
|
|
617
|
+
- **`@getcontextual/shopify-sdk/vanilla`** - Vanilla JavaScript API
|
|
618
|
+
- **`@getcontextual/shopify-sdk/react`** - React hooks and components
|
|
619
|
+
|
|
620
|
+
## Troubleshooting
|
|
621
|
+
|
|
622
|
+
### Events Not Being Tracked
|
|
623
|
+
|
|
624
|
+
1. Ensure `init()` or `<TrackerProvider>` is called before tracking
|
|
625
|
+
2. Check browser console for initialization warnings
|
|
626
|
+
3. Verify `merchantId` matches your Shopify store domain
|
|
627
|
+
4. If using SSR, ensure tracking is called in `useEffect` or after hydration
|
|
628
|
+
|
|
629
|
+
### GTM Events Not Appearing
|
|
630
|
+
|
|
631
|
+
1. Verify `gtmContainerId` is correct
|
|
632
|
+
2. Inspect `window.dataLayer` in browser console
|
|
633
|
+
3. Use GTM preview mode to see if events are received
|
|
634
|
+
4. Check Network tab for GTM script requests
|
|
635
|
+
|
|
636
|
+
### TypeScript Errors
|
|
637
|
+
|
|
638
|
+
1. Ensure TypeScript version is compatible (^5.9.2)
|
|
639
|
+
2. Use the correct import path for your framework
|
|
640
|
+
3. Types are automatically included - no additional setup needed
|
|
641
|
+
|
|
642
|
+
### React Strict Mode
|
|
643
|
+
|
|
644
|
+
The SDK is React Strict Mode safe. You may see duplicate event tracking in development mode, but this won't happen in production.
|
|
645
|
+
|
|
646
|
+
## License
|
|
647
|
+
|
|
648
|
+
Licensed under the Apache License, Version 2.0. See [LICENSE](./LICENSE) for details.
|
|
649
|
+
|
|
650
|
+
## Support
|
|
651
|
+
|
|
652
|
+
For issues, questions, or contributions, please open an issue on the repository.
|
package/THIRD_PARTY
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
This package bundles the following third-party dependencies from @contextual/browser:
|
|
2
|
+
|
|
3
|
+
## js-cookie
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright: Copyright (c) 2014-2017 Klaus Hartl, Fagner Brack
|
|
6
|
+
https://github.com/js-cookie/js-cookie
|
|
7
|
+
|
|
8
|
+
## uuid
|
|
9
|
+
License: MIT
|
|
10
|
+
Copyright: Copyright (c) 2010-2020 Robert Kieffer and other contributors
|
|
11
|
+
https://github.com/uuidjs/uuid
|
|
12
|
+
|
|
13
|
+
## tldts
|
|
14
|
+
License: MIT
|
|
15
|
+
Copyright: Copyright (c) 2017 Thomas Parisot, 2018 Rémi Berson
|
|
16
|
+
https://github.com/remusao/tldts
|
|
17
|
+
|
|
18
|
+
## ua-parser-js
|
|
19
|
+
License: MIT
|
|
20
|
+
Copyright: Copyright (c) 2012-2021 Faisal Salman <f@faisalman.com>
|
|
21
|
+
https://github.com/faisalman/ua-parser-js
|
|
22
|
+
|
|
23
|
+
## detectincognitojs
|
|
24
|
+
License: MIT
|
|
25
|
+
Copyright: Copyright (c) 2020-present Jacek Laskowski
|
|
26
|
+
https://github.com/JLaskowski/detectincognitojs
|
|
27
|
+
|
|
28
|
+
## is-email
|
|
29
|
+
License: MIT
|
|
30
|
+
Copyright: Copyright (c) 2020-present
|
|
31
|
+
https://github.com/hapijs/isemail
|
|
32
|
+
|
|
33
|
+
## is-network-error
|
|
34
|
+
License: MIT
|
|
35
|
+
Copyright: Copyright (c) 2019 Sindre Sorhus
|
|
36
|
+
https://github.com/sindresorhus/is-network-error
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
Full license texts for these packages can be found in their respective npm packages:
|
|
41
|
+
- npm view <package-name> license
|
|
42
|
+
- Or visit: https://www.npmjs.com/package/<package-name>
|
|
43
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { HeadlessShopifyTracker } from "./tracker";
|
|
2
|
+
export type { HeadlessShopifyConfig } from "./tracker";
|
|
3
|
+
export type * from "./types";
|
|
4
|
+
export { Tracking } from "@contextual/browser";
|
|
5
|
+
export type { BrowserEvent } from "@contextual/browser";
|
|
6
|
+
export * from "./react";
|
|
7
|
+
export * from "./vanilla";
|