@behindthescenes/analytics 0.0.9 → 0.0.10
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 +480 -162
- package/dist/manifest.json +2 -2
- package/docs/advanced-setup.md +499 -0
- package/docs/api-reference.md +498 -0
- package/docs/frameworks.md +936 -0
- package/package.json +2 -1
|
@@ -0,0 +1,936 @@
|
|
|
1
|
+
# Framework Setup Guide
|
|
2
|
+
|
|
3
|
+
This guide covers setting up the BTS Analytics SDK in popular frontend frameworks.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Plain JavaScript / HTML](#plain-javascript--html)
|
|
8
|
+
- [React](#react)
|
|
9
|
+
- [Next.js](#nextjs)
|
|
10
|
+
- [Remix](#remix)
|
|
11
|
+
- [Vue](#vue)
|
|
12
|
+
- [Svelte](#svelte)
|
|
13
|
+
- [Angular](#angular)
|
|
14
|
+
- [SolidJS](#solidjs)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Plain JavaScript / HTML
|
|
19
|
+
|
|
20
|
+
The simplest way to get started - no build tools required.
|
|
21
|
+
|
|
22
|
+
### Browser Bundle (CDN)
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<script async src="https://api.bts.it.com/v2/website/analytics/sdk/analytics/latest/browser/browser.js"></script>
|
|
26
|
+
<script>
|
|
27
|
+
window.btsDataLayer = window.btsDataLayer || [];
|
|
28
|
+
function bts(){window.btsDataLayer.push(arguments);}
|
|
29
|
+
bts("js", new Date());
|
|
30
|
+
bts("config", "your-public-site-key", {
|
|
31
|
+
autoPageviews: true
|
|
32
|
+
});
|
|
33
|
+
</script>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### ES Module Import
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<script type="module">
|
|
40
|
+
import { createBTSAnalytics } from "https://app.behindthescenes.com/sdk/analytics/latest/browser/browser.js";
|
|
41
|
+
|
|
42
|
+
window.btsAnalytics = createBTSAnalytics({
|
|
43
|
+
siteKey: "your-public-site-key"
|
|
44
|
+
});
|
|
45
|
+
</script>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Usage Examples
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
// Track a custom event
|
|
52
|
+
document.querySelector("#watch-preview")?.addEventListener("click", () => {
|
|
53
|
+
window.btsAnalytics.track("watch_preview_clicked", {
|
|
54
|
+
placement: "hero",
|
|
55
|
+
contentId: "intro-preview",
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Track a standard conversion
|
|
60
|
+
window.btsAnalytics.trackStandard("lead", {
|
|
61
|
+
formId: "newsletter",
|
|
62
|
+
placement: "footer",
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Identify a visitor
|
|
66
|
+
window.btsAnalytics.identify("user_123", {
|
|
67
|
+
email: "fan@example.com",
|
|
68
|
+
plan: "pro",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Track page views manually
|
|
72
|
+
window.btsAnalytics.page("/pricing");
|
|
73
|
+
|
|
74
|
+
// Start a funnel and decorate links
|
|
75
|
+
async function goToCheckout() {
|
|
76
|
+
const { journeyToken } = await window.btsAnalytics.startFunnel("/landing");
|
|
77
|
+
const checkoutUrl = window.btsAnalytics.decorateUrl(
|
|
78
|
+
"https://behindthescenes.com/checkout/your-offer",
|
|
79
|
+
journeyToken,
|
|
80
|
+
);
|
|
81
|
+
window.location.assign(checkoutUrl);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Flush before redirecting
|
|
85
|
+
async function trackAndRedirect() {
|
|
86
|
+
window.btsAnalytics.track("checkout_cta_clicked", {
|
|
87
|
+
placement: "hero",
|
|
88
|
+
});
|
|
89
|
+
await window.btsAnalytics.flushNow();
|
|
90
|
+
window.location.assign("/checkout");
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## React
|
|
97
|
+
|
|
98
|
+
### Setup: Provider Pattern
|
|
99
|
+
|
|
100
|
+
Create `BTSAnalyticsProvider.tsx`:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import {
|
|
104
|
+
BTSAnalytics,
|
|
105
|
+
createBTSAnalytics,
|
|
106
|
+
} from "@behindthescenes/analytics";
|
|
107
|
+
import {
|
|
108
|
+
createContext,
|
|
109
|
+
PropsWithChildren,
|
|
110
|
+
useContext,
|
|
111
|
+
useMemo,
|
|
112
|
+
} from "react";
|
|
113
|
+
|
|
114
|
+
const BTSAnalyticsContext = createContext<BTSAnalytics | null>(null);
|
|
115
|
+
|
|
116
|
+
export function BTSAnalyticsProvider({ children }: PropsWithChildren) {
|
|
117
|
+
const analytics = useMemo(
|
|
118
|
+
() =>
|
|
119
|
+
createBTSAnalytics({
|
|
120
|
+
siteKey: "your-public-site-key",
|
|
121
|
+
autoPageviews: true,
|
|
122
|
+
}),
|
|
123
|
+
[],
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<BTSAnalyticsContext.Provider value={analytics}>
|
|
128
|
+
{children}
|
|
129
|
+
</BTSAnalyticsContext.Provider>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function useBTSAnalytics() {
|
|
134
|
+
const analytics = useContext(BTSAnalyticsContext);
|
|
135
|
+
if (!analytics) {
|
|
136
|
+
throw new Error("useBTSAnalytics must be used inside BTSAnalyticsProvider");
|
|
137
|
+
}
|
|
138
|
+
return analytics;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Wrap your app:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
import { BTSAnalyticsProvider } from "./BTSAnalyticsProvider";
|
|
146
|
+
|
|
147
|
+
function App() {
|
|
148
|
+
return (
|
|
149
|
+
<BTSAnalyticsProvider>
|
|
150
|
+
<YourApp />
|
|
151
|
+
</BTSAnalyticsProvider>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Usage Examples
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { useBTSAnalytics } from "./BTSAnalyticsProvider";
|
|
160
|
+
|
|
161
|
+
// Track a custom event
|
|
162
|
+
function TrackEventExample() {
|
|
163
|
+
const analytics = useBTSAnalytics();
|
|
164
|
+
|
|
165
|
+
function trackPreviewClick() {
|
|
166
|
+
analytics.track("watch_preview_clicked", {
|
|
167
|
+
placement: "hero",
|
|
168
|
+
contentId: "intro-preview",
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return <button onClick={trackPreviewClick}>Watch Preview</button>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Track a standard conversion
|
|
176
|
+
function LeadEventExample() {
|
|
177
|
+
const analytics = useBTSAnalytics();
|
|
178
|
+
|
|
179
|
+
function trackLead() {
|
|
180
|
+
analytics.trackStandard("lead", {
|
|
181
|
+
formId: "newsletter",
|
|
182
|
+
placement: "footer",
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return <button onClick={trackLead}>Subscribe</button>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Identify a visitor
|
|
190
|
+
function IdentifyExample() {
|
|
191
|
+
const analytics = useBTSAnalytics();
|
|
192
|
+
|
|
193
|
+
function identifyUser() {
|
|
194
|
+
analytics.identify("user_123", {
|
|
195
|
+
email: "fan@example.com",
|
|
196
|
+
plan: "pro",
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return <button onClick={identifyUser}>Sign In</button>;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Track page views manually
|
|
204
|
+
function ManualPageView() {
|
|
205
|
+
const analytics = useBTSAnalytics();
|
|
206
|
+
|
|
207
|
+
function trackCurrentPage() {
|
|
208
|
+
analytics.page(window.location.pathname + window.location.search);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return <button onClick={trackCurrentPage}>Track Page</button>;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Start a funnel and decorate links
|
|
215
|
+
function JourneyExample() {
|
|
216
|
+
const analytics = useBTSAnalytics();
|
|
217
|
+
|
|
218
|
+
async function goToCheckout() {
|
|
219
|
+
const { journeyToken } = await analytics.startFunnel("/landing");
|
|
220
|
+
const checkoutUrl = analytics.decorateUrl(
|
|
221
|
+
"https://behindthescenes.com/checkout/your-offer",
|
|
222
|
+
journeyToken,
|
|
223
|
+
);
|
|
224
|
+
window.location.assign(checkoutUrl);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return <button onClick={goToCheckout}>Checkout</button>;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Flush before redirecting
|
|
231
|
+
function FlushExample() {
|
|
232
|
+
const analytics = useBTSAnalytics();
|
|
233
|
+
|
|
234
|
+
async function trackAndRedirect() {
|
|
235
|
+
analytics.track("checkout_cta_clicked", {
|
|
236
|
+
placement: "hero",
|
|
237
|
+
});
|
|
238
|
+
await analytics.flushNow();
|
|
239
|
+
window.location.assign("/checkout");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return <button onClick={trackAndRedirect}>Go to Checkout</button>;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Next.js
|
|
249
|
+
|
|
250
|
+
### Setup: App Router
|
|
251
|
+
|
|
252
|
+
Create `app/providers.tsx`:
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
"use client";
|
|
256
|
+
|
|
257
|
+
import {
|
|
258
|
+
BTSAnalytics,
|
|
259
|
+
createBTSAnalytics,
|
|
260
|
+
} from "@behindthescenes/analytics";
|
|
261
|
+
import {
|
|
262
|
+
createContext,
|
|
263
|
+
PropsWithChildren,
|
|
264
|
+
useContext,
|
|
265
|
+
useMemo,
|
|
266
|
+
} from "react";
|
|
267
|
+
|
|
268
|
+
const BTSAnalyticsContext = createContext<BTSAnalytics | null>(null);
|
|
269
|
+
|
|
270
|
+
export function Providers({ children }: PropsWithChildren) {
|
|
271
|
+
const analytics = useMemo(
|
|
272
|
+
() =>
|
|
273
|
+
createBTSAnalytics({
|
|
274
|
+
siteKey: "your-public-site-key",
|
|
275
|
+
autoPageviews: true,
|
|
276
|
+
}),
|
|
277
|
+
[],
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<BTSAnalyticsContext.Provider value={analytics}>
|
|
282
|
+
{children}
|
|
283
|
+
</BTSAnalyticsContext.Provider>
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function useBTSAnalytics() {
|
|
288
|
+
const analytics = useContext(BTSAnalyticsContext);
|
|
289
|
+
if (!analytics) {
|
|
290
|
+
throw new Error("useBTSAnalytics must be used inside Providers");
|
|
291
|
+
}
|
|
292
|
+
return analytics;
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Update `app/layout.tsx`:
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
import { Providers } from "./providers";
|
|
300
|
+
|
|
301
|
+
export default function RootLayout({
|
|
302
|
+
children,
|
|
303
|
+
}: {
|
|
304
|
+
children: React.ReactNode;
|
|
305
|
+
}) {
|
|
306
|
+
return (
|
|
307
|
+
<html lang="en">
|
|
308
|
+
<body>
|
|
309
|
+
<Providers>{children}</Providers>
|
|
310
|
+
</body>
|
|
311
|
+
</html>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Setup: Pages Router
|
|
317
|
+
|
|
318
|
+
Create a custom `_app.tsx`:
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
import type { AppProps } from "next/app";
|
|
322
|
+
import { BTSAnalyticsProvider } from "../components/BTSAnalyticsProvider";
|
|
323
|
+
|
|
324
|
+
export default function MyApp({ Component, pageProps }: AppProps) {
|
|
325
|
+
return (
|
|
326
|
+
<BTSAnalyticsProvider>
|
|
327
|
+
<Component {...pageProps} />
|
|
328
|
+
</BTSAnalyticsProvider>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Usage Examples
|
|
334
|
+
|
|
335
|
+
Same as the React examples above, using `useBTSAnalytics()` from `@/app/providers`.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Remix
|
|
340
|
+
|
|
341
|
+
### Setup: Root Provider
|
|
342
|
+
|
|
343
|
+
Update `app/root.tsx`:
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
import { createBTSAnalytics } from "@behindthescenes/analytics";
|
|
347
|
+
import { useEffect, useMemo } from "react";
|
|
348
|
+
import { Outlet, useLocation } from "react-router";
|
|
349
|
+
|
|
350
|
+
// Create singleton instance
|
|
351
|
+
const analytics = createBTSAnalytics({
|
|
352
|
+
siteKey: "your-public-site-key",
|
|
353
|
+
autoPageviews: false, // Handle manually for Remix
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
export function Layout() {
|
|
357
|
+
const location = useLocation();
|
|
358
|
+
const path = useMemo(
|
|
359
|
+
() => location.pathname + location.search,
|
|
360
|
+
[location.pathname, location.search],
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
useEffect(() => {
|
|
364
|
+
analytics.page(path);
|
|
365
|
+
}, [path]);
|
|
366
|
+
|
|
367
|
+
return <Outlet />;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export { analytics };
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Usage Examples
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
import { analytics } from "~/root";
|
|
377
|
+
|
|
378
|
+
// Track a custom event
|
|
379
|
+
export function TrackEventExample() {
|
|
380
|
+
function trackPreviewClick() {
|
|
381
|
+
analytics.track("watch_preview_clicked", {
|
|
382
|
+
placement: "hero",
|
|
383
|
+
contentId: "intro-preview",
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return <button onClick={trackPreviewClick}>Watch Preview</button>;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Track a standard conversion
|
|
391
|
+
export function LeadEventExample() {
|
|
392
|
+
function trackLead() {
|
|
393
|
+
analytics.trackStandard("lead", {
|
|
394
|
+
formId: "newsletter",
|
|
395
|
+
placement: "footer",
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return <button onClick={trackLead}>Subscribe</button>;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Identify a visitor
|
|
403
|
+
export function IdentifyExample() {
|
|
404
|
+
function identifyUser() {
|
|
405
|
+
analytics.identify("user_123", {
|
|
406
|
+
email: "fan@example.com",
|
|
407
|
+
plan: "pro",
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return <button onClick={identifyUser}>Sign In</button>;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Track page views manually
|
|
415
|
+
export function ManualPageView() {
|
|
416
|
+
function trackCurrentPage() {
|
|
417
|
+
analytics.page(window.location.pathname + window.location.search);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return <button onClick={trackCurrentPage}>Track Page</button>;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Start a funnel and decorate links
|
|
424
|
+
export function JourneyExample() {
|
|
425
|
+
async function goToCheckout() {
|
|
426
|
+
const { journeyToken } = await analytics.startFunnel("/landing");
|
|
427
|
+
const checkoutUrl = analytics.decorateUrl(
|
|
428
|
+
"https://behindthescenes.com/checkout/your-offer",
|
|
429
|
+
journeyToken,
|
|
430
|
+
);
|
|
431
|
+
window.location.assign(checkoutUrl);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return <button onClick={goToCheckout}>Checkout</button>;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Flush before redirecting
|
|
438
|
+
export function FlushExample() {
|
|
439
|
+
async function trackAndRedirect() {
|
|
440
|
+
analytics.track("checkout_cta_clicked", {
|
|
441
|
+
placement: "hero",
|
|
442
|
+
});
|
|
443
|
+
await analytics.flushNow();
|
|
444
|
+
window.location.assign("/checkout");
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return <button onClick={trackAndRedirect}>Go to Checkout</button>;
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## Vue
|
|
454
|
+
|
|
455
|
+
### Setup: Plugin Pattern
|
|
456
|
+
|
|
457
|
+
Create `src/plugins/bts-analytics.ts`:
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
import { createBTSAnalytics } from "@behindthescenes/analytics";
|
|
461
|
+
import type { App } from "vue";
|
|
462
|
+
|
|
463
|
+
const analytics = createBTSAnalytics({
|
|
464
|
+
siteKey: "your-public-site-key",
|
|
465
|
+
autoPageviews: true,
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
export function installBTSAnalytics(app: App) {
|
|
469
|
+
app.provide("btsAnalytics", analytics);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export { analytics };
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Register in `main.ts`:
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
import { createApp } from "vue";
|
|
479
|
+
import App from "./App.vue";
|
|
480
|
+
import { installBTSAnalytics } from "./plugins/bts-analytics";
|
|
481
|
+
|
|
482
|
+
const app = createApp(App);
|
|
483
|
+
app.use(installBTSAnalytics);
|
|
484
|
+
app.mount("#app");
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Composable
|
|
488
|
+
|
|
489
|
+
Create `src/composables/useAnalytics.ts`:
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
import { inject } from "vue";
|
|
493
|
+
import type { BTSAnalytics } from "@behindthescenes/analytics";
|
|
494
|
+
|
|
495
|
+
export function useAnalytics(): BTSAnalytics {
|
|
496
|
+
const analytics = inject<BTSAnalytics>("btsAnalytics");
|
|
497
|
+
if (!analytics) {
|
|
498
|
+
throw new Error("useAnalytics must be called inside a Vue app with BTS Analytics installed");
|
|
499
|
+
}
|
|
500
|
+
return analytics;
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Usage Examples
|
|
505
|
+
|
|
506
|
+
```vue
|
|
507
|
+
<script setup lang="ts">
|
|
508
|
+
import { useAnalytics } from "@/composables/useAnalytics";
|
|
509
|
+
|
|
510
|
+
const analytics = useAnalytics();
|
|
511
|
+
|
|
512
|
+
// Track a custom event
|
|
513
|
+
function trackPreviewClick() {
|
|
514
|
+
analytics.track("watch_preview_clicked", {
|
|
515
|
+
placement: "hero",
|
|
516
|
+
contentId: "intro-preview",
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Track a standard conversion
|
|
521
|
+
function trackLead() {
|
|
522
|
+
analytics.trackStandard("lead", {
|
|
523
|
+
formId: "newsletter",
|
|
524
|
+
placement: "footer",
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Identify a visitor
|
|
529
|
+
function identifyUser() {
|
|
530
|
+
analytics.identify("user_123", {
|
|
531
|
+
email: "fan@example.com",
|
|
532
|
+
plan: "pro",
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Track page views manually
|
|
537
|
+
function trackCurrentPage() {
|
|
538
|
+
analytics.page(window.location.pathname + window.location.search);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Start a funnel and decorate links
|
|
542
|
+
async function goToCheckout() {
|
|
543
|
+
const { journeyToken } = await analytics.startFunnel("/landing");
|
|
544
|
+
const checkoutUrl = analytics.decorateUrl(
|
|
545
|
+
"https://behindthescenes.com/checkout/your-offer",
|
|
546
|
+
journeyToken,
|
|
547
|
+
);
|
|
548
|
+
window.location.assign(checkoutUrl);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Flush before redirecting
|
|
552
|
+
async function trackAndRedirect() {
|
|
553
|
+
analytics.track("checkout_cta_clicked", {
|
|
554
|
+
placement: "hero",
|
|
555
|
+
});
|
|
556
|
+
await analytics.flushNow();
|
|
557
|
+
window.location.assign("/checkout");
|
|
558
|
+
}
|
|
559
|
+
</script>
|
|
560
|
+
|
|
561
|
+
<template>
|
|
562
|
+
<button @click="trackPreviewClick">Watch Preview</button>
|
|
563
|
+
<button @click="trackLead">Subscribe</button>
|
|
564
|
+
<button @click="identifyUser">Sign In</button>
|
|
565
|
+
<button @click="trackCurrentPage">Track Page</button>
|
|
566
|
+
<button @click="goToCheckout">Checkout</button>
|
|
567
|
+
<button @click="trackAndRedirect">Go to Checkout</button>
|
|
568
|
+
</template>
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
## Svelte
|
|
574
|
+
|
|
575
|
+
### Setup: Store Pattern
|
|
576
|
+
|
|
577
|
+
Create `src/lib/analytics.ts`:
|
|
578
|
+
|
|
579
|
+
```typescript
|
|
580
|
+
import { createBTSAnalytics } from "@behindthescenes/analytics";
|
|
581
|
+
|
|
582
|
+
export const analytics = createBTSAnalytics({
|
|
583
|
+
siteKey: "your-public-site-key",
|
|
584
|
+
autoPageviews: true,
|
|
585
|
+
});
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Usage in Components
|
|
589
|
+
|
|
590
|
+
```svelte
|
|
591
|
+
<script lang="ts">
|
|
592
|
+
import { analytics } from "$lib/analytics";
|
|
593
|
+
|
|
594
|
+
// Track a custom event
|
|
595
|
+
function trackPreviewClick() {
|
|
596
|
+
analytics.track("watch_preview_clicked", {
|
|
597
|
+
placement: "hero",
|
|
598
|
+
contentId: "intro-preview",
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Track a standard conversion
|
|
603
|
+
function trackLead() {
|
|
604
|
+
analytics.trackStandard("lead", {
|
|
605
|
+
formId: "newsletter",
|
|
606
|
+
placement: "footer",
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Identify a visitor
|
|
611
|
+
function identifyUser() {
|
|
612
|
+
analytics.identify("user_123", {
|
|
613
|
+
email: "fan@example.com",
|
|
614
|
+
plan: "pro",
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Start a funnel and decorate links
|
|
619
|
+
async function goToCheckout() {
|
|
620
|
+
const { journeyToken } = await analytics.startFunnel("/landing");
|
|
621
|
+
const checkoutUrl = analytics.decorateUrl(
|
|
622
|
+
"https://behindthescenes.com/checkout/your-offer",
|
|
623
|
+
journeyToken,
|
|
624
|
+
);
|
|
625
|
+
window.location.assign(checkoutUrl);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Flush before redirecting
|
|
629
|
+
async function trackAndRedirect() {
|
|
630
|
+
analytics.track("checkout_cta_clicked", {
|
|
631
|
+
placement: "hero",
|
|
632
|
+
});
|
|
633
|
+
await analytics.flushNow();
|
|
634
|
+
window.location.assign("/checkout");
|
|
635
|
+
}
|
|
636
|
+
</script>
|
|
637
|
+
|
|
638
|
+
<button on:click={trackPreviewClick}>Watch Preview</button>
|
|
639
|
+
<button on:click={trackLead}>Subscribe</button>
|
|
640
|
+
<button on:click={identifyUser}>Sign In</button>
|
|
641
|
+
<button on:click={goToCheckout}>Checkout</button>
|
|
642
|
+
<button on:click={trackAndRedirect}>Go to Checkout</button>
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### SvelteKit Setup
|
|
646
|
+
|
|
647
|
+
For SvelteKit with SSR, create a client-side only module:
|
|
648
|
+
|
|
649
|
+
```typescript
|
|
650
|
+
// src/lib/analytics.client.ts
|
|
651
|
+
import { browser } from "$app/environment";
|
|
652
|
+
import { createBTSAnalytics } from "@behindthescenes/analytics";
|
|
653
|
+
|
|
654
|
+
export const analytics = browser
|
|
655
|
+
? createBTSAnalytics({
|
|
656
|
+
siteKey: "your-public-site-key",
|
|
657
|
+
autoPageviews: true,
|
|
658
|
+
})
|
|
659
|
+
: null;
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
Use in components:
|
|
663
|
+
|
|
664
|
+
```svelte
|
|
665
|
+
<script lang="ts">
|
|
666
|
+
import { analytics } from "$lib/analytics.client";
|
|
667
|
+
|
|
668
|
+
function trackEvent() {
|
|
669
|
+
analytics?.track("button_clicked", { buttonId: "cta" });
|
|
670
|
+
}
|
|
671
|
+
</script>
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
---
|
|
675
|
+
|
|
676
|
+
## Angular
|
|
677
|
+
|
|
678
|
+
### Setup: Service Pattern
|
|
679
|
+
|
|
680
|
+
Create `src/app/services/bts-analytics.service.ts`:
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
import { Injectable } from "@angular/core";
|
|
684
|
+
import { createBTSAnalytics, BTSAnalytics } from "@behindthescenes/analytics";
|
|
685
|
+
|
|
686
|
+
@Injectable({
|
|
687
|
+
providedIn: "root",
|
|
688
|
+
})
|
|
689
|
+
export class BTSAnalyticsService {
|
|
690
|
+
private analytics: BTSAnalytics;
|
|
691
|
+
|
|
692
|
+
constructor() {
|
|
693
|
+
this.analytics = createBTSAnalytics({
|
|
694
|
+
siteKey: "your-public-site-key",
|
|
695
|
+
autoPageviews: true,
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
track(eventName: string, properties?: Record<string, unknown>) {
|
|
700
|
+
this.analytics.track(eventName, properties);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
trackStandard(eventName: string, properties?: Record<string, unknown>) {
|
|
704
|
+
this.analytics.trackStandard(eventName as any, properties);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
identify(userId: string, traits?: Record<string, unknown>) {
|
|
708
|
+
this.analytics.identify(userId, traits);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
page(path?: string) {
|
|
712
|
+
this.analytics.page(path);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
async startFunnel(entryPath?: string) {
|
|
716
|
+
return this.analytics.startFunnel(entryPath);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
decorateUrl(url: string, journeyToken?: string) {
|
|
720
|
+
return this.analytics.decorateUrl(url, journeyToken);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
async flushNow() {
|
|
724
|
+
return this.analytics.flushNow();
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### Usage in Components
|
|
730
|
+
|
|
731
|
+
```typescript
|
|
732
|
+
import { Component } from "@angular/core";
|
|
733
|
+
import { BTSAnalyticsService } from "./services/bts-analytics.service";
|
|
734
|
+
|
|
735
|
+
@Component({
|
|
736
|
+
selector: "app-example",
|
|
737
|
+
template: `
|
|
738
|
+
<button (click)="trackPreviewClick()">Watch Preview</button>
|
|
739
|
+
<button (click)="trackLead()">Subscribe</button>
|
|
740
|
+
<button (click)="identifyUser()">Sign In</button>
|
|
741
|
+
<button (click)="goToCheckout()">Checkout</button>
|
|
742
|
+
`,
|
|
743
|
+
})
|
|
744
|
+
export class ExampleComponent {
|
|
745
|
+
constructor(private analytics: BTSAnalyticsService) {}
|
|
746
|
+
|
|
747
|
+
trackPreviewClick() {
|
|
748
|
+
this.analytics.track("watch_preview_clicked", {
|
|
749
|
+
placement: "hero",
|
|
750
|
+
contentId: "intro-preview",
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
trackLead() {
|
|
755
|
+
this.analytics.trackStandard("lead", {
|
|
756
|
+
formId: "newsletter",
|
|
757
|
+
placement: "footer",
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
identifyUser() {
|
|
762
|
+
this.analytics.identify("user_123", {
|
|
763
|
+
email: "fan@example.com",
|
|
764
|
+
plan: "pro",
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
async goToCheckout() {
|
|
769
|
+
const { journeyToken } = await this.analytics.startFunnel("/landing");
|
|
770
|
+
const checkoutUrl = this.analytics.decorateUrl(
|
|
771
|
+
"https://behindthescenes.com/checkout/your-offer",
|
|
772
|
+
journeyToken,
|
|
773
|
+
);
|
|
774
|
+
window.location.assign(checkoutUrl);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
---
|
|
780
|
+
|
|
781
|
+
## SolidJS
|
|
782
|
+
|
|
783
|
+
### Setup: Context Pattern
|
|
784
|
+
|
|
785
|
+
Create `src/context/BTSAnalyticsContext.tsx`:
|
|
786
|
+
|
|
787
|
+
```tsx
|
|
788
|
+
import { createBTSAnalytics, BTSAnalytics } from "@behindthescenes/analytics";
|
|
789
|
+
import { createContext, useContext, ParentComponent } from "solid-js";
|
|
790
|
+
|
|
791
|
+
const BTSAnalyticsContext = createContext<BTSAnalytics>();
|
|
792
|
+
|
|
793
|
+
export const BTSAnalyticsProvider: ParentComponent = (props) => {
|
|
794
|
+
const analytics = createBTSAnalytics({
|
|
795
|
+
siteKey: "your-public-site-key",
|
|
796
|
+
autoPageviews: true,
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
return (
|
|
800
|
+
<BTSAnalyticsContext.Provider value={analytics}>
|
|
801
|
+
{props.children}
|
|
802
|
+
</BTSAnalyticsContext.Provider>
|
|
803
|
+
);
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
export function useBTSAnalytics() {
|
|
807
|
+
const analytics = useContext(BTSAnalyticsContext);
|
|
808
|
+
if (!analytics) {
|
|
809
|
+
throw new Error("useBTSAnalytics must be used inside BTSAnalyticsProvider");
|
|
810
|
+
}
|
|
811
|
+
return analytics;
|
|
812
|
+
}
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### Usage in Components
|
|
816
|
+
|
|
817
|
+
```tsx
|
|
818
|
+
import { useBTSAnalytics } from "../context/BTSAnalyticsContext";
|
|
819
|
+
|
|
820
|
+
export function TrackEventExample() {
|
|
821
|
+
const analytics = useBTSAnalytics();
|
|
822
|
+
|
|
823
|
+
function trackPreviewClick() {
|
|
824
|
+
analytics.track("watch_preview_clicked", {
|
|
825
|
+
placement: "hero",
|
|
826
|
+
contentId: "intro-preview",
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
return <button onClick={trackPreviewClick}>Watch Preview</button>;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
export function LeadEventExample() {
|
|
834
|
+
const analytics = useBTSAnalytics();
|
|
835
|
+
|
|
836
|
+
function trackLead() {
|
|
837
|
+
analytics.trackStandard("lead", {
|
|
838
|
+
formId: "newsletter",
|
|
839
|
+
placement: "footer",
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return <button onClick={trackLead}>Subscribe</button>;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
export function IdentifyExample() {
|
|
847
|
+
const analytics = useBTSAnalytics();
|
|
848
|
+
|
|
849
|
+
function identifyUser() {
|
|
850
|
+
analytics.identify("user_123", {
|
|
851
|
+
email: "fan@example.com",
|
|
852
|
+
plan: "pro",
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
return <button onClick={identifyUser}>Sign In</button>;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
export function JourneyExample() {
|
|
860
|
+
const analytics = useBTSAnalytics();
|
|
861
|
+
|
|
862
|
+
async function goToCheckout() {
|
|
863
|
+
const { journeyToken } = await analytics.startFunnel("/landing");
|
|
864
|
+
const checkoutUrl = analytics.decorateUrl(
|
|
865
|
+
"https://behindthescenes.com/checkout/your-offer",
|
|
866
|
+
journeyToken,
|
|
867
|
+
);
|
|
868
|
+
window.location.assign(checkoutUrl);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
return <button onClick={goToCheckout}>Checkout</button>;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
export function FlushExample() {
|
|
875
|
+
const analytics = useBTSAnalytics();
|
|
876
|
+
|
|
877
|
+
async function trackAndRedirect() {
|
|
878
|
+
analytics.track("checkout_cta_clicked", {
|
|
879
|
+
placement: "hero",
|
|
880
|
+
});
|
|
881
|
+
await analytics.flushNow();
|
|
882
|
+
window.location.assign("/checkout");
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return <button onClick={trackAndRedirect}>Go to Checkout</button>;
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
---
|
|
890
|
+
|
|
891
|
+
## Server-Side Rendering (SSR) Considerations
|
|
892
|
+
|
|
893
|
+
When using frameworks with SSR (Next.js, Remix, SvelteKit), keep these points in mind:
|
|
894
|
+
|
|
895
|
+
### 1. Client-Only Initialization
|
|
896
|
+
|
|
897
|
+
The SDK requires browser APIs (`window`, `document`, `localStorage`). Always initialize on the client:
|
|
898
|
+
|
|
899
|
+
```typescript
|
|
900
|
+
// Good
|
|
901
|
+
const analytics = typeof window !== "undefined"
|
|
902
|
+
? createBTSAnalytics({ siteKey: "..." })
|
|
903
|
+
: null;
|
|
904
|
+
|
|
905
|
+
// Or use framework utilities
|
|
906
|
+
import { browser } from "$app/environment"; // SvelteKit
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
### 2. Disable Auto-Pageviews for Manual Control
|
|
910
|
+
|
|
911
|
+
```typescript
|
|
912
|
+
const analytics = createBTSAnalytics({
|
|
913
|
+
siteKey: "your-public-site-key",
|
|
914
|
+
autoPageviews: false, // Handle manually for SSR frameworks
|
|
915
|
+
});
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
### 3. Router Integration
|
|
919
|
+
|
|
920
|
+
Integrate with your framework's router for accurate page tracking:
|
|
921
|
+
|
|
922
|
+
- **Next.js**: Use `usePathname()` in a client component
|
|
923
|
+
- **Remix**: Use `useLocation()` in `root.tsx`
|
|
924
|
+
- **SvelteKit**: Use `$page.url` in `+layout.svelte`
|
|
925
|
+
|
|
926
|
+
### 4. Cleanup on Navigation
|
|
927
|
+
|
|
928
|
+
The SDK handles most cleanup automatically, but call `destroy()` when unmounting:
|
|
929
|
+
|
|
930
|
+
```typescript
|
|
931
|
+
import { onCleanup } from "solid-js";
|
|
932
|
+
|
|
933
|
+
onCleanup(() => {
|
|
934
|
+
analytics?.destroy();
|
|
935
|
+
});
|
|
936
|
+
```
|