@dotcms/analytics 1.2.4 → 1.2.5

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 CHANGED
@@ -1,645 +1,562 @@
1
- # dotCMS Content Analytics SDK (@dotcms/analytics)
1
+ # @dotcms/analytics
2
2
 
3
- Lightweight JavaScript SDK for tracking content-aware events in dotCMS. Works in vanilla JS and React apps. Angular & Vue support coming soon.
3
+ Content Analytics SDK for tracking content-aware events in dotCMS-powered React applications.
4
4
 
5
- ## 🚀 Quick Start
5
+ ## Quick Start
6
6
 
7
- ### Standalone (Script Tag)
8
-
9
- ```html
10
- <script
11
- src="ca.min.js"
12
- data-analytics-server="https://demo.dotcms.com"
13
- data-analytics-auth="SITE_AUTH"
14
- data-analytics-auto-page-view="true"
15
- data-analytics-debug="false"></script>
16
- ```
17
-
18
- **npm (ES Module)**
7
+ ### 1. Install
19
8
 
20
9
  ```bash
21
10
  npm install @dotcms/analytics
22
11
  ```
23
12
 
24
- ```javascript
25
- import { initializeContentAnalytics } from '@dotcms/analytics';
26
-
27
- const analytics = initializeContentAnalytics({
28
- siteAuth: 'SITE_AUTH',
29
- server: 'https://demo.dotcms.com'
30
- });
13
+ ### 2. Create a centralized config file
31
14
 
32
- analytics.track('page-loaded');
15
+ ```javascript
16
+ // src/config/analytics.config.js
17
+ export const analyticsConfig = {
18
+ siteAuth: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY,
19
+ server: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST,
20
+ autoPageView: true,
21
+ debug: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_DEBUG === 'true',
22
+ impressions: true,
23
+ clicks: true
24
+ };
33
25
  ```
34
26
 
35
- ### React (In Development)
27
+ ### 3. Add automatic page view tracking to your layout
36
28
 
37
- ```bash
38
- npm install @dotcms/analytics
39
- ```
40
-
41
- ```tsx
29
+ ```jsx
30
+ // src/app/layout.js
42
31
  import { DotContentAnalytics } from '@dotcms/analytics/react';
43
-
44
- const config = {
45
- siteAuth: 'SITE_AUTH',
46
- server: 'https://demo.dotcms.com',
47
- autoPageView: true // Optional, default is true in React
48
- };
49
-
50
- export function AppRoot() {
51
- return <DotContentAnalytics config={config} />;
32
+ import { analyticsConfig } from '@/config/analytics.config';
33
+
34
+ export default function RootLayout({ children }) {
35
+ return (
36
+ <html lang="en">
37
+ <body>
38
+ <DotContentAnalytics config={analyticsConfig} />
39
+ {children}
40
+ </body>
41
+ </html>
42
+ );
52
43
  }
53
44
  ```
54
45
 
55
- > **Note:** React API is subject to change during development.
46
+ ### 4. Track events in your components
56
47
 
57
- ## 📘 Core Concepts
48
+ ```jsx
49
+ 'use client';
58
50
 
59
- ### Page Views
60
-
61
- The `pageView()` method tracks page navigation events. **It automatically enriches the event with comprehensive data**, including:
51
+ import { useContentAnalytics } from '@dotcms/analytics/react';
52
+ import { analyticsConfig } from '@/config/analytics.config';
62
53
 
63
- - **Page data**: URL, title, referrer, path, protocol, search params, etc.
64
- - **Device data**: Screen resolution, viewport size, language, user agent
65
- - **UTM parameters**: Campaign tracking data (source, medium, campaign, etc.)
66
- - **Context**: Site key, session ID, user ID, timestamp
54
+ function ContactForm() {
55
+ const { conversion } = useContentAnalytics(analyticsConfig);
67
56
 
68
- You can optionally include custom data that will be sent **in addition** to all the automatic enrichment.
57
+ const handleSubmit = (e) => {
58
+ e.preventDefault();
59
+ // ... submit form logic ...
69
60
 
70
- **Method signature:**
61
+ // Track conversion ONLY after successful submission
62
+ conversion('form-submit', {
63
+ formName: 'contact-us',
64
+ formType: 'lead-gen'
65
+ });
66
+ };
71
67
 
72
- ```typescript
73
- pageView(customData?: Record<string, unknown>): void
68
+ return <form onSubmit={handleSubmit}>{/* form fields */}</form>;
69
+ }
74
70
  ```
75
71
 
76
- **Behavior:**
72
+ ## Understanding the Components
77
73
 
78
- - **Standalone (IIFE)**: Auto-tracked only if `data-analytics-auto-page-view="true"`; otherwise call `window.dotAnalytics.pageView()` manually.
79
- - **React**: In development (API may change)
80
- - Custom data is optional and gets attached to the pageview event under the `custom` property alongside all automatically captured data.
74
+ The SDK exports two React primitives. Understanding their roles is critical for correct usage.
81
75
 
82
- ### Conversion Tracking
83
-
84
- The `conversion()` method tracks user conversions (purchases, downloads, sign-ups, etc.) from your application.
76
+ ### `<DotContentAnalytics />` -- Automatic Page View Tracker
85
77
 
86
- **⚠️ IMPORTANT: Conversion events are business events that should only be tracked after a successful action or completed goal.** Tracking conversions on clicks or attempts (before success) diminishes their value as conversion metrics. Only track conversions when:
78
+ - Its **only purpose** is to automatically track page views on route changes
79
+ - It is **NOT** a React Context Provider
80
+ - It does **NOT** share config with child components
81
+ - Place it once in your root layout
87
82
 
88
- - ✅ Purchase is completed and payment is confirmed
89
- - ✅ Download is successfully completed
90
- - ✅ Sign-up form is submitted and account is created
91
- - ✅ Form submission is successful and data is saved
92
- - ✅ Any business goal is actually achieved
83
+ ### `useContentAnalytics(config)` -- Manual Tracking Hook
93
84
 
94
- **Method signature:**
85
+ - Used for custom events, conversions, and manual page views
86
+ - **ALWAYS requires `config` as a parameter** -- it does not read from context
87
+ - Import the centralized config in every component that uses the hook
95
88
 
96
- ```typescript
97
- conversion(name: string): void
98
- conversion(name: string, options?: Record<string, unknown>): void
99
- ```
100
-
101
- **Usage examples:**
102
-
103
- ```typescript
104
- // Basic conversion tracking (after successful download)
105
- analytics.conversion('download');
106
-
107
- // Conversion with custom metadata (after successful purchase)
108
- analytics.conversion('purchase', {
109
- value: 99.99,
110
- currency: 'USD',
111
- category: 'ecommerce',
112
- productId: 'SKU-12345'
113
- });
114
-
115
- // Conversion with additional context (after successful signup)
116
- analytics.conversion('signup', {
117
- source: 'homepage',
118
- plan: 'premium'
119
- });
120
- ```
121
-
122
- **Event payload structure:**
89
+ ```jsx
90
+ // Every component that tracks events must import config explicitly
91
+ import { useContentAnalytics } from '@dotcms/analytics/react';
92
+ import { analyticsConfig } from '@/config/analytics.config';
123
93
 
124
- ```json
125
- {
126
- "event_type": "conversion",
127
- "local_time": "2025-10-01T16:08:33-04:00",
128
- "data": {
129
- "conversion": { "name": "download" },
130
- "page": { "url": "...", "title": "..." },
131
- "custom": { "value": 99.99, "currency": "USD", "source": "homepage" }
132
- }
133
- }
94
+ const { track, pageView, conversion } = useContentAnalytics(analyticsConfig);
134
95
  ```
135
96
 
136
- **Important:**
97
+ > **Why centralize config?** You must import it in each component, but having a single file prevents duplication and makes updates easier.
137
98
 
138
- - `name` is required and identifies the conversion type
139
- - All properties in `options` go into the `custom` object
140
- - Page data (url, title) is automatically added by the SDK
141
- - **Only track conversions after successful completion of business goals**
99
+ ## Configuration
142
100
 
143
- ### Custom Events
101
+ ### Environment Variables
144
102
 
145
- The `track()` method allows you to track any custom user action with a unique event name and optional properties.
103
+ Add these to your `.env.local` file:
146
104
 
147
- **Method signature:**
148
-
149
- ```typescript
150
- track(eventName: string, properties?: Record<string, unknown>): void
105
+ ```bash
106
+ NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY=YOUR_ANALYTICS_SITE_KEY
107
+ NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST=http://localhost:8080
108
+ NEXT_PUBLIC_DOTCMS_ANALYTICS_DEBUG=true
151
109
  ```
152
110
 
153
- **Important:**
111
+ | Variable | Description |
112
+ | --- | --- |
113
+ | `NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY` | Site auth key from the Content Analytics app in dotCMS |
114
+ | `NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST` | URL where your dotCMS instance is running |
115
+ | `NEXT_PUBLIC_DOTCMS_ANALYTICS_DEBUG` | Set to `"true"` to enable verbose console logging |
154
116
 
155
- - `eventName` cannot be `"pageview"` or `"conversion"` (reserved for specific tracking methods)
156
- - `eventName` should be a descriptive string like `"button-click"`, `"form-submit"`, `"video-play"`, etc.
157
- - `properties` is optional and can contain any custom data relevant to the event
117
+ ### Config Options
158
118
 
159
- ### Sessions
160
-
161
- - 30-minute timeout
162
- - Resets at midnight UTC
163
- - New session if UTM campaign changes
164
-
165
- ### Identity
166
-
167
- - Anonymous user ID persisted across sessions
168
- - Stored in `dot_analytics_user_id`
169
-
170
- ## ⚙️ Configuration Options
171
-
172
- | Option | Type | Required | Default | Description |
173
- | -------------- | --------------------------- | -------- | ----------------------------------- | ---------------------------------------------------------------- |
174
- | `siteAuth` | `string` | ✅ | - | Site auth from dotCMS Analytics app |
175
- | `server` | `string` | ✅ | - | Your dotCMS server URL |
176
- | `debug` | `boolean` | ❌ | `false` | Enable verbose logging |
177
- | `autoPageView` | `boolean` | ❌ | React: `true` / Standalone: `false` | Auto track page views on route changes |
178
- | `queueConfig` | `QueueConfig` | ❌ | See below | Event batching configuration |
179
- | `impressions` | `ImpressionConfig\|boolean` | ❌ | `false` | Content impression tracking (disabled by default) |
180
- | `clicks` | `boolean` | ❌ | `false` | Content click tracking with 300ms throttle (disabled by default) |
119
+ | Option | Type | Required | Default | Description |
120
+ | --- | --- | --- | --- | --- |
121
+ | `siteAuth` | `string` | Yes | -- | Site auth from dotCMS Analytics app |
122
+ | `server` | `string` | Yes | -- | Your dotCMS server URL |
123
+ | `debug` | `boolean` | No | `false` | Enable verbose logging |
124
+ | `autoPageView` | `boolean` | No | `true` | Auto track page views on route changes |
125
+ | `queue` | `QueueConfig \| false` | No | See below | Event batching configuration |
126
+ | `impressions` | `ImpressionConfig \| boolean` | No | `false` | Content impression tracking |
127
+ | `clicks` | `boolean` | No | `false` | Content click tracking (300ms throttle) |
181
128
 
182
129
  ### Queue Configuration
183
130
 
184
- The `queueConfig` option controls event batching:
131
+ Controls how events are batched before being sent to the server:
185
132
 
186
133
  - **`false`**: Disable queuing, send events immediately
187
134
  - **`undefined` (default)**: Enable queuing with default settings
188
- - **`QueueConfig` object**: Enable queuing with custom settings
135
+ - **`QueueConfig` object**: Custom settings
189
136
 
190
- | Option | Type | Default | Description |
191
- | ---------------- | -------- | ------- | ------------------------------------------------ |
192
- | `eventBatchSize` | `number` | `15` | Max events per batch - auto-sends when reached |
193
- | `flushInterval` | `number` | `5000` | Time between flushes - sends pending events (ms) |
137
+ | Option | Type | Default | Description |
138
+ | --- | --- | --- | --- |
139
+ | `eventBatchSize` | `number` | `15` | Max events per batch -- auto-sends when reached |
140
+ | `flushInterval` | `number` | `5000` | Time between flushes in milliseconds |
194
141
 
195
- **How it works:**
142
+ How it works:
196
143
 
197
- - Send immediately when `eventBatchSize` reached (e.g., 15 events)
198
- - Send pending events every `flushInterval` (e.g., 5 seconds)
199
- - Auto-flush on page navigation/close using `visibilitychange` + `pagehide` events
200
- - Example: If you have 10 events and 5 seconds pass → sends those 10
201
-
202
- **About page unload handling:**
203
-
204
- The SDK uses modern APIs (`visibilitychange` + `pagehide`) instead of `beforeunload`/`unload` to ensure:
205
-
206
- - ✅ Better reliability on mobile devices
207
- - ✅ Compatible with browser back/forward cache (bfcache)
208
- - ✅ Events are sent via `navigator.sendBeacon()` for guaranteed delivery
209
- - ✅ No negative impact on page performance
210
-
211
- **Example: Disable queuing for immediate sends**
144
+ - Sends immediately when `eventBatchSize` is reached
145
+ - Sends pending events every `flushInterval` milliseconds
146
+ - Auto-flushes on page navigation/close using `visibilitychange` + `pagehide`
147
+ - Uses `navigator.sendBeacon()` for reliable delivery on page unload
212
148
 
213
149
  ```javascript
214
- const analytics = initializeContentAnalytics({
215
- siteAuth: 'abc123',
216
- server: 'https://your-dotcms.com',
217
- queue: false // Send events immediately without batching
218
- });
219
- ```
220
-
221
- **Example: Custom queue config**
150
+ // Disable queuing (send immediately)
151
+ export const analyticsConfig = {
152
+ siteAuth: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY,
153
+ server: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST,
154
+ queue: false
155
+ };
222
156
 
223
- ```javascript
224
- const analytics = initializeContentAnalytics({
225
- siteAuth: 'abc123',
226
- server: 'https://your-dotcms.com',
157
+ // Custom queue settings
158
+ export const analyticsConfig = {
159
+ siteAuth: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY,
160
+ server: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST,
227
161
  queue: {
228
- eventBatchSize: 10, // Auto-send when 10 events queued
229
- flushInterval: 3000 // Or send every 3 seconds
162
+ eventBatchSize: 10,
163
+ flushInterval: 3000
230
164
  }
231
- });
165
+ };
232
166
  ```
233
167
 
234
- ### Impression Tracking Configuration
168
+ ### Impression Tracking
235
169
 
236
- The `impressions` option controls automatic tracking of content visibility:
170
+ Controls automatic tracking of content visibility in the viewport:
237
171
 
238
- - **`false` or `undefined` (default)**: Impression tracking disabled
239
- - **`true`**: Enable tracking with default settings
240
- - **`ImpressionConfig` object**: Enable tracking with custom settings
172
+ - **`false` or `undefined` (default)**: Disabled
173
+ - **`true`**: Enabled with default settings
174
+ - **`ImpressionConfig` object**: Custom settings
241
175
 
242
- | Option | Type | Default | Description |
243
- | --------------------- | -------- | ------- | ----------------------------------------- |
244
- | `visibilityThreshold` | `number` | `0.5` | Min percentage visible (0.0 to 1.0) |
245
- | `dwellMs` | `number` | `750` | Min time visible in milliseconds |
246
- | `maxNodes` | `number` | `1000` | Max elements to track (performance limit) |
176
+ | Option | Type | Default | Description |
177
+ | --- | --- | --- | --- |
178
+ | `visibilityThreshold` | `number` | `0.5` | Min percentage visible (0.0 to 1.0) |
179
+ | `dwellMs` | `number` | `750` | Min time visible in milliseconds |
180
+ | `maxNodes` | `number` | `1000` | Max elements to track (performance limit) |
247
181
 
248
- **How it works:**
182
+ How it works:
249
183
 
250
- - Tracks contentlets marked with `dotcms-contentlet` class and `data-dot-*` attributes (e.g., `data-dot-identifier`, `data-dot-inode`, `data-dot-type`)
251
- - Uses Intersection Observer API for high performance and battery efficiency
252
- - Only fires when element is ≥50% visible for ≥750ms (configurable)
253
- - Only tracks during active tab (respects page visibility)
254
- - One impression per contentlet per session (no duplicates)
255
- - ✅ Respects user consent settings
256
- - ✅ Automatically disabled in dotCMS editor mode
257
-
258
- **Example: Enable with defaults**
184
+ - Tracks contentlets marked with `dotcms-contentlet` class and `data-dot-*` attributes
185
+ - Uses Intersection Observer API for performance
186
+ - Only fires when an element is visible for the configured threshold and dwell time
187
+ - One impression per contentlet per session (no duplicates)
188
+ - Automatically disabled in dotCMS editor mode
259
189
 
260
190
  ```javascript
261
- const analytics = initializeContentAnalytics({
262
- siteAuth: 'abc123',
263
- server: 'https://your-dotcms.com',
264
- impressions: true // 50% visible, 750ms dwell, 1000 max nodes
265
- });
266
- ```
267
-
268
- **Example: Custom thresholds**
191
+ // Enable with defaults (50% visible, 750ms dwell)
192
+ export const analyticsConfig = {
193
+ // ...required fields
194
+ impressions: true
195
+ };
269
196
 
270
- ```javascript
271
- const analytics = initializeContentAnalytics({
272
- siteAuth: 'abc123',
273
- server: 'https://your-dotcms.com',
197
+ // Custom thresholds
198
+ export const analyticsConfig = {
199
+ // ...required fields
274
200
  impressions: {
275
- visibilityThreshold: 0.7, // Require 70% visible
276
- dwellMs: 1000, // Must be visible for 1 second
277
- maxNodes: 500 // Track max 500 elements
201
+ visibilityThreshold: 0.7,
202
+ dwellMs: 1000,
203
+ maxNodes: 500
278
204
  }
279
- });
205
+ };
280
206
  ```
281
207
 
282
- **Example: Disable tracking**
208
+ ### Click Tracking
283
209
 
284
- ```javascript
285
- const analytics = initializeContentAnalytics({
286
- siteAuth: 'abc123',
287
- server: 'https://your-dotcms.com',
288
- impressions: false // Explicitly disabled (also default if omitted)
289
- });
290
- ```
210
+ Controls automatic tracking of user clicks on content elements:
291
211
 
292
- ### Click Tracking Configuration
212
+ - **`false` or `undefined` (default)**: Disabled
213
+ - **`true`**: Enabled with 300ms throttle
293
214
 
294
- The `clicks` option controls automatic tracking of user interactions with content elements:
215
+ How it works:
295
216
 
296
- - **`false` or `undefined` (default)**: Click tracking disabled
297
- - **`true`**: Enable tracking with default settings (300ms throttle)
217
+ - Tracks clicks on `<a>` and `<button>` elements within contentlets
218
+ - Contentlets must be marked with `dotcms-contentlet` class and `data-dot-*` attributes
219
+ - Captures semantic attributes (`href`, `aria-label`, `data-*`) and excludes CSS classes
220
+ - Throttles rapid clicks to prevent duplicates (300ms)
221
+ - Automatically disabled in dotCMS editor mode
298
222
 
299
- **How it works:**
223
+ **Captured data per click:**
300
224
 
301
- - Tracks clicks on `<a>` and `<button>` elements within contentlets
302
- - Contentlets must be marked with `dotcms-contentlet` class and `data-dot-*` attributes (e.g., `data-dot-identifier`, `data-dot-inode`, `data-dot-type`)
303
- - Captures semantic attributes (`href`, `aria-label`, `data-*`) and excludes CSS classes
304
- - ✅ Throttles rapid clicks to prevent duplicate tracking (300ms fixed)
305
- - ✅ One click event per interaction
306
- - ✅ Respects user consent settings
307
- - ✅ Automatically disabled in dotCMS editor mode
225
+ - **Content Info**: `identifier`, `inode`, `title`, `content_type`
226
+ - **Element Info**: `text`, `type` (a/button), `id`, `class`, `href`, `attributes`
227
+ - **Position Info**: `viewport_offset_pct`, `dom_index`
308
228
 
309
- **Captured Data:**
229
+ You can enrich click data using `data-*` attributes in your HTML:
310
230
 
311
- For each click, the SDK captures:
231
+ ```html
232
+ <a
233
+ href="/signup"
234
+ id="cta-signup"
235
+ data-category="primary-cta"
236
+ data-campaign="summer-sale"
237
+ aria-label="Sign up for free trial">
238
+ Start Free Trial
239
+ </a>
312
240
 
313
- - **Content Info**: `identifier`, `inode`, `title`, `content_type`
314
- - **Element Info**:
315
- - `text` - Button/link text (truncated to 100 chars)
316
- - `type` - Element type (`a` or `button`)
317
- - `id` - Element ID (required by backend, empty string if not present)
318
- - `class` - Element CSS classes (required by backend, empty string if not present)
319
- - `href` - Link destination as written in HTML (e.g., `/signup` not `http://...`, only for `<a>`, empty string for buttons)
320
- - `attributes` - Additional useful attributes (see below)
321
- - **Position Info**:
322
- - `viewport_offset_pct` - Position relative to viewport (0-100%)
323
- - `dom_index` - Element position in DOM
241
+ <button data-action="download" data-file-type="pdf" data-category="lead-magnet">
242
+ Download Whitepaper
243
+ </button>
244
+ ```
324
245
 
325
- **Attributes Array:**
246
+ ## Core Concepts
326
247
 
327
- > **Note**: The `attributes` field is formatted as an array of `'key:value'` strings (e.g., `['data-category:primary-cta', 'aria-label:Sign up']`) for efficient serialization and backend parsing.
248
+ ### Event Types
328
249
 
329
- The `attributes` array captures additional semantic data in `'key:value'` string format:
250
+ The SDK sends four types of events, identified by `event_type`:
330
251
 
331
- **Included** (semantic/analytics value):
252
+ | Event Type | Trigger | Requires |
253
+ | --- | --- | --- |
254
+ | `pageview` | Automatically on route change, or manually via `pageView()` | `autoPageView: true` or manual call |
255
+ | `content_impression` | When a contentlet becomes visible in the viewport | `impressions` config enabled |
256
+ | `content_click` | When a user clicks a link/button inside a contentlet | `clicks` config enabled |
257
+ | `conversion` | Explicitly via `conversion()` after a successful business action | Manual call |
332
258
 
333
- - `data-*` - Custom data attributes (e.g., `'data-category:primary-cta'`)
334
- - `aria-*` - Accessibility attributes (e.g., `'aria-label:Sign up now'`)
335
- - `title` - Element title
336
- - `target` - Link target (e.g., `'target:_blank'`)
337
- - Any other standard HTML attributes
259
+ ### Page Views
338
260
 
339
- **Excluded** (to avoid duplication):
261
+ The `pageView()` method tracks page navigation events. It **automatically enriches** the event with:
340
262
 
341
- - `class` - Already captured as top-level property
342
- - `id` - Already captured as top-level property
343
- - `href` - Already captured as top-level property
344
- - `data-dot-*` - Internal SDK attributes (e.g., `data-dot-identifier`, `data-dot-inode`, `data-dot-type`)
263
+ - **Page data**: URL, title, referrer, path, protocol, search params
264
+ - **Device data**: Screen resolution, viewport size, language, user agent
265
+ - **UTM parameters**: Campaign tracking data (source, medium, campaign, term, content)
266
+ - **Context**: Site key, session ID, user ID, timestamp
345
267
 
346
- **Example: Enable click tracking**
268
+ You can optionally pass custom data that will be sent **in addition** to all the automatic enrichment.
347
269
 
348
- ```javascript
349
- const analytics = initializeContentAnalytics({
350
- siteAuth: 'abc123',
351
- server: 'https://your-dotcms.com',
352
- clicks: true // Enable with 300ms throttle (fixed)
353
- });
354
- ```
270
+ ### Conversion Tracking
355
271
 
356
- **Example: Adding Custom Analytics Metadata**
272
+ The `conversion()` method tracks user conversions (purchases, downloads, sign-ups, etc.).
357
273
 
358
- Use `data-*` attributes to enrich click tracking with custom metadata:
274
+ **Only track conversions after a successful action or completed goal.** Tracking on clicks or attempts (before success) diminishes their value as conversion metrics. Track when:
359
275
 
360
- ```html
361
- <!-- Primary CTA with category -->
362
- <a
363
- href="/signup"
364
- id="cta-signup"
365
- data-category="primary-cta"
366
- data-campaign="summer-sale"
367
- aria-label="Sign up for free trial">
368
- Start Free Trial →
369
- </a>
276
+ - Purchase is completed and payment is confirmed
277
+ - Download is successfully completed
278
+ - Sign-up form is submitted and account is created
279
+ - Any business goal is actually achieved
370
280
 
371
- <!-- Product link with metadata -->
372
- <a href="/products/123" data-product-id="123" data-product-name="Premium Plan" data-price="29.99">
373
- View Product
374
- </a>
281
+ ### Custom Events
375
282
 
376
- <!-- Button with custom tracking -->
377
- <button data-action="download" data-file-type="pdf" data-category="lead-magnet">
378
- Download Whitepaper
379
- </button>
380
- ```
283
+ The `track()` method tracks any custom user action with a unique event name and optional properties.
381
284
 
382
- **Resulting Click Event:**
285
+ - `eventName` cannot be `"pageview"` or `"conversion"` (reserved)
286
+ - Use descriptive names: `"button-click"`, `"form-submit"`, `"video-play"`, etc.
383
287
 
384
- ```json
385
- {
386
- "content": {
387
- "identifier": "abc123",
388
- "inode": "xyz789",
389
- "title": "Product Page",
390
- "content_type": "Page"
391
- },
392
- "element": {
393
- "text": "Start Free Trial →",
394
- "type": "a",
395
- "id": "cta-signup",
396
- "class": "btn btn-primary text-white",
397
- "href": "/signup",
398
- "attributes": [
399
- "data-category:primary-cta",
400
- "data-campaign:summer-sale",
401
- "aria-label:Sign up for free trial",
402
- "target:_blank"
403
- ]
404
- },
405
- "position": {
406
- "viewport_offset_pct": 45.2,
407
- "dom_index": 2
408
- }
409
- }
410
- ```
288
+ ### Sessions
411
289
 
412
- **Example: Disable tracking**
290
+ - 30-minute inactivity timeout
291
+ - Resets at midnight UTC
292
+ - New session if UTM campaign changes
413
293
 
414
- ```javascript
415
- const analytics = initializeContentAnalytics({
416
- siteAuth: 'abc123',
417
- server: 'https://your-dotcms.com',
418
- clicks: false // Explicitly disabled (also default if omitted)
419
- });
420
- ```
294
+ ### Identity
295
+
296
+ - Anonymous user ID persisted across sessions
297
+ - Stored in `dot_analytics_user_id`
421
298
 
422
- ## 🛠️ Usage Examples
299
+ ## Usage Examples
423
300
 
424
- ### Vanilla JavaScript
301
+ ### Automatic Page View Tracking
425
302
 
426
- **Basic Page View**
303
+ Add `DotContentAnalytics` to your root layout. No additional code is needed -- page views are tracked on every route change.
427
304
 
428
- ```javascript
429
- // After init with the <script> tag, dotAnalytics is added to the window
430
- // Automatically captures: page, device, UTM, context (session, user, site)
431
- window.dotAnalytics.pageView();
305
+ ```jsx
306
+ // src/app/layout.js
307
+ import { DotContentAnalytics } from '@dotcms/analytics/react';
308
+ import { analyticsConfig } from '@/config/analytics.config';
309
+
310
+ export default function RootLayout({ children }) {
311
+ return (
312
+ <html lang="en">
313
+ <body>
314
+ <DotContentAnalytics config={analyticsConfig} />
315
+ {children}
316
+ </body>
317
+ </html>
318
+ );
319
+ }
432
320
  ```
433
321
 
434
- **Page View with Additional Custom Data**
322
+ ### Manual Page View with Custom Data
435
323
 
436
- ```javascript
437
- // All automatic data (page, device, UTM, context) is STILL captured
438
- // Plus your custom properties are added under 'custom'
439
- window.dotAnalytics.pageView({
440
- contentType: 'blog',
441
- category: 'technology',
442
- author: 'john-doe',
443
- wordCount: 1500
444
- });
445
-
446
- // The server receives: page + device + utm + context + custom (your data)
447
- ```
324
+ ```jsx
325
+ 'use client';
448
326
 
449
- **Track Custom Events**
327
+ import { useEffect } from 'react';
328
+ import { useContentAnalytics } from '@dotcms/analytics/react';
329
+ import { analyticsConfig } from '@/config/analytics.config';
450
330
 
451
- ```javascript
452
- // Basic event tracking
453
- window.dotAnalytics.track('cta-click', {
454
- button: 'Buy Now',
455
- location: 'hero-section'
456
- });
457
-
458
- // Track form submission
459
- window.dotAnalytics.track('form-submit', {
460
- formName: 'contact-form',
461
- formType: 'lead-gen'
462
- });
463
-
464
- // Track video interaction
465
- window.dotAnalytics.track('video-play', {
466
- videoId: 'intro-video',
467
- duration: 120,
468
- autoplay: false
469
- });
470
- ```
331
+ function BlogPost({ post }) {
332
+ const { pageView } = useContentAnalytics(analyticsConfig);
471
333
 
472
- **Advanced: Manual Init with Custom Properties**
334
+ useEffect(() => {
335
+ pageView({
336
+ contentType: 'blog',
337
+ category: post.category,
338
+ author: post.author,
339
+ wordCount: post.wordCount
340
+ });
341
+ }, []);
473
342
 
474
- ```javascript
475
- const analytics = initializeContentAnalytics({
476
- siteAuth: 'abc123',
477
- server: 'https://your-dotcms.com',
478
- debug: true,
479
- autoPageView: false
480
- });
481
-
482
- // Track custom events with properties
483
- analytics.track('product-view', {
484
- productId: 'SKU-12345',
485
- category: 'Electronics',
486
- price: 299.99,
487
- inStock: true
488
- });
489
-
490
- // Manual page view with custom data
491
- // Automatic enrichment (page, device, UTM, context) + your custom data
492
- analytics.pageView({
493
- section: 'checkout',
494
- step: 'payment',
495
- cartValue: 299.99
496
- });
343
+ return <article>{/* post content */}</article>;
344
+ }
497
345
  ```
498
346
 
499
- ### React
347
+ ### Custom Event Tracking
500
348
 
501
- > **Note:** React integration is currently in development. The API may change.
349
+ ```jsx
350
+ 'use client';
502
351
 
503
- **Basic Setup**
352
+ import { useContentAnalytics } from '@dotcms/analytics/react';
353
+ import { analyticsConfig } from '@/config/analytics.config';
504
354
 
505
- ```tsx
506
- import { DotContentAnalytics } from '@dotcms/analytics/react';
355
+ function CallToAction() {
356
+ const { track } = useContentAnalytics(analyticsConfig);
507
357
 
508
- const config = {
509
- siteAuth: 'SITE_KEY',
510
- server: 'https://demo.dotcms.com',
511
- autoPageView: true
512
- };
358
+ const handleClick = () => {
359
+ track('cta-click', {
360
+ button: 'Buy Now',
361
+ location: 'hero-section',
362
+ price: 299.99
363
+ });
364
+ };
513
365
 
514
- export function AppRoot() {
515
- return <DotContentAnalytics config={config} />;
366
+ return <button onClick={handleClick}>Buy Now</button>;
516
367
  }
517
368
  ```
518
369
 
519
- **Using the Hook**
370
+ ### Conversion Tracking (Real-World Example)
371
+
372
+ This example is based on the Contact Us form in the [Next.js example app](https://github.com/dotCMS/core/tree/main/examples/nextjs):
373
+
374
+ ```jsx
375
+ 'use client';
520
376
 
521
- ```tsx
377
+ import { useState } from 'react';
522
378
  import { useContentAnalytics } from '@dotcms/analytics/react';
379
+ import { analyticsConfig } from '@/config/analytics.config';
380
+
381
+ export default function ContactUs({ description }) {
382
+ const [isSubmitting, setIsSubmitting] = useState(false);
383
+ const [isSuccess, setIsSuccess] = useState(false);
384
+ const { conversion } = useContentAnalytics(analyticsConfig);
385
+
386
+ const handleSubmit = (e) => {
387
+ e.preventDefault();
388
+ setIsSubmitting(true);
389
+
390
+ // Simulate form submission
391
+ setTimeout(() => {
392
+ setIsSuccess(true);
393
+
394
+ // Track conversion ONLY after successful submission
395
+ conversion('form-submit', {
396
+ formName: 'contact-us',
397
+ formType: 'lead-gen'
398
+ });
399
+ }, 3000);
400
+ };
523
401
 
524
- function MyComponent() {
525
- const { track, pageView } = useContentAnalytics({
526
- siteAuth: 'SITE_AUTH',
527
- server: 'https://demo.dotcms.com'
528
- });
402
+ return (
403
+ <form onSubmit={handleSubmit}>
404
+ {/* form fields */}
405
+ <button type="submit" disabled={isSubmitting}>
406
+ {isSubmitting ? 'Submitting...' : 'Submit'}
407
+ </button>
408
+ </form>
409
+ );
410
+ }
411
+ ```
529
412
 
530
- // Track custom events (same API as vanilla JS)
531
- const handleClick = () => {
532
- track('button-click', { label: 'CTA Button' });
413
+ ### E-commerce Purchase Conversion
414
+
415
+ ```jsx
416
+ 'use client';
417
+
418
+ import { useContentAnalytics } from '@dotcms/analytics/react';
419
+ import { analyticsConfig } from '@/config/analytics.config';
420
+
421
+ function CheckoutButton({ product, quantity }) {
422
+ const { conversion } = useContentAnalytics(analyticsConfig);
423
+
424
+ const handlePurchase = async () => {
425
+ // Process payment...
426
+ const result = await processPayment(product, quantity);
427
+
428
+ if (result.success) {
429
+ // Track conversion ONLY after confirmed payment
430
+ conversion('purchase', {
431
+ value: product.price * quantity,
432
+ currency: 'USD',
433
+ productId: product.sku,
434
+ category: product.category
435
+ });
436
+ }
533
437
  };
534
438
 
535
- return <button onClick={handleClick}>Click Me</button>;
439
+ return <button onClick={handlePurchase}>Complete Purchase</button>;
536
440
  }
537
441
  ```
538
442
 
539
443
  ## API Reference
540
444
 
445
+ ### `<DotContentAnalytics />`
446
+
447
+ React component for automatic page view tracking on route changes.
448
+
541
449
  ```typescript
450
+ interface DotContentAnalyticsProps {
451
+ config: DotCMSAnalyticsConfig;
452
+ }
453
+ ```
454
+
455
+ Place once in your root layout. Wraps the internal tracker in `<Suspense>` for Next.js App Router compatibility.
456
+
457
+ ### `useContentAnalytics(config)`
458
+
459
+ React hook that returns tracking methods. **Always requires config as a parameter.**
460
+
461
+ ```typescript
462
+ function useContentAnalytics(config: DotCMSAnalyticsConfig): DotCMSAnalytics;
463
+
542
464
  interface DotCMSAnalytics {
543
- /**
544
- * Track a page view event with optional custom data
545
- * @param customData - Optional object with custom properties to attach to the pageview
546
- */
465
+ /** Track a page view with optional custom data */
547
466
  pageView: (customData?: Record<string, unknown>) => void;
548
467
 
549
- /**
550
- * Track a custom event
551
- * @param eventName - Name of the custom event (cannot be "pageview" or "conversion")
552
- * @param properties - Optional object with event-specific properties
553
- */
468
+ /** Track a custom event (eventName cannot be "pageview" or "conversion") */
554
469
  track: (eventName: string, properties?: Record<string, unknown>) => void;
555
470
 
556
- /**
557
- * Track a conversion event (purchase, download, sign-up, etc.)
558
- * ⚠️ IMPORTANT: Only track conversions after successful completion of business goals
559
- * @param name - Name of the conversion (e.g., "purchase", "download", "signup")
560
- * @param options - Optional object with conversion metadata (all properties go into custom object)
561
- */
471
+ /** Track a conversion after a successful business action */
562
472
  conversion: (name: string, options?: Record<string, unknown>) => void;
563
473
  }
564
474
  ```
565
475
 
566
- ### Page View Event Structure
476
+ ### `DotCMSAnalyticsConfig`
567
477
 
568
- When you call `pageView(customData?)`, the SDK **automatically enriches** the event with comprehensive data and sends:
478
+ ```typescript
479
+ interface DotCMSAnalyticsConfig {
480
+ server: string;
481
+ siteAuth: string;
482
+ debug?: boolean;
483
+ autoPageView?: boolean;
484
+ queue?: QueueConfig | false;
485
+ impressions?: ImpressionConfig | boolean;
486
+ clicks?: boolean;
487
+ }
488
+
489
+ interface QueueConfig {
490
+ eventBatchSize?: number;
491
+ flushInterval?: number;
492
+ }
493
+
494
+ interface ImpressionConfig {
495
+ visibilityThreshold?: number;
496
+ dwellMs?: number;
497
+ maxNodes?: number;
498
+ }
499
+ ```
500
+
501
+ ### Event Payload Structures
502
+
503
+ #### Page View Event
504
+
505
+ When you call `pageView(customData?)`, the SDK sends:
569
506
 
570
507
  ```typescript
571
508
  {
572
- context: { // 🤖 AUTOMATIC - Identity & Session
573
- site_key: string; // Your site key
574
- session_id: string; // Current session ID
575
- user_id: string; // Anonymous user ID
576
- device: { // 🤖 AUTOMATIC - Device & Browser Info
577
- screen_resolution: string; // Screen size
578
- language: string; // Browser language
579
- viewport_width: string; // Viewport width
580
- viewport_height: string; // Viewport height
509
+ context: {
510
+ site_key: string; // Your site key
511
+ session_id: string; // Current session ID
512
+ user_id: string; // Anonymous user ID
513
+ device: {
514
+ screen_resolution: string;
515
+ language: string;
516
+ viewport_width: string;
517
+ viewport_height: string;
581
518
  }
582
519
  },
583
520
  events: [{
584
521
  event_type: "pageview",
585
- local_time: string, // 🤖 AUTOMATIC - ISO 8601 timestamp with timezone
522
+ local_time: string, // ISO 8601 timestamp with timezone
586
523
  data: {
587
- page: { // 🤖 AUTOMATIC - Page Information
588
- url: string; // Full URL
589
- title: string; // Page title
590
- referrer: string; // Referrer URL
591
- path: string; // Path
592
- doc_host: string; // Hostname
593
- doc_protocol: string; // Protocol (http/https)
594
- doc_search: string; // Query string
595
- doc_hash: string; // URL hash
596
- doc_encoding: string; // Character encoding
524
+ page: { // Captured automatically
525
+ url: string;
526
+ title: string;
527
+ referrer: string;
528
+ path: string;
529
+ doc_host: string;
530
+ doc_protocol: string;
531
+ doc_search: string;
532
+ doc_hash: string;
533
+ doc_encoding: string;
597
534
  },
598
- utm?: { // 🤖 AUTOMATIC - Campaign Tracking (if present in URL)
599
- source: string; // utm_source
600
- medium: string; // utm_medium
601
- campaign: string; // utm_campaign
602
- term: string; // utm_term
603
- content: string; // utm_content
535
+ utm?: { // Captured automatically (if present in URL)
536
+ source: string;
537
+ medium: string;
538
+ campaign: string;
539
+ term: string;
540
+ content: string;
604
541
  },
605
- custom?: { // 👤 YOUR DATA (optional)
606
- // Any custom properties you pass to pageView(customData)
607
- contentType?: string;
608
- category?: string;
609
- author?: string;
610
- // ... any other properties
542
+ custom?: { // Your optional data from pageView(customData)
543
+ // Any properties you pass
611
544
  }
612
545
  }
613
546
  }]
614
547
  }
615
548
  ```
616
549
 
617
- **Key Points:**
550
+ #### Custom Event
618
551
 
619
- - 🤖 Most data is captured **automatically** - you don't need to provide it
620
- - 👤 `custom` is where **your optional data** goes
621
- - All automatic data is always captured, even if you don't pass `customData`
622
-
623
- ### Custom Event Structure
624
-
625
- When you call `track(eventName, properties)`, the following structure is sent:
552
+ When you call `track(eventName, properties)`:
626
553
 
627
554
  ```typescript
628
555
  {
629
- context: {
630
- site_key: string; // Your site key
631
- session_id: string; // Current session ID
632
- user_id: string; // Anonymous user ID
633
- device: { // 🤖 AUTOMATIC - Device & Browser Info
634
- screen_resolution: string; // Screen size
635
- language: string; // Browser language
636
- viewport_width: string; // Viewport width
637
- viewport_height: string; // Viewport height
638
- }
639
- },
556
+ context: { /* same as above */ },
640
557
  events: [{
641
- event_type: string, // Your custom event name
642
- local_time: string, // ISO 8601 timestamp
558
+ event_type: string, // Your custom event name
559
+ local_time: string,
643
560
  data: {
644
561
  custom: {
645
562
  // Your properties object
@@ -649,96 +566,139 @@ When you call `track(eventName, properties)`, the following structure is sent:
649
566
  }
650
567
  ```
651
568
 
652
- ### Conversion Event Structure
569
+ #### Conversion Event
653
570
 
654
- When you call `conversion(name, options)`, the following structure is sent:
571
+ When you call `conversion(name, options)`:
655
572
 
656
573
  ```typescript
657
574
  {
658
- context: {
659
- site_key: string; // Your site key
660
- session_id: string; // Current session ID
661
- user_id: string; // Anonymous user ID
662
- device: { // 🤖 AUTOMATIC - Device & Browser Info
663
- screen_resolution: string; // Screen size
664
- language: string; // Browser language
665
- viewport_width: string; // Viewport width
666
- viewport_height: string; // Viewport height
667
- }
668
- },
575
+ context: { /* same as above */ },
669
576
  events: [{
670
577
  event_type: "conversion",
671
- local_time: string, // ISO 8601 timestamp
578
+ local_time: string,
672
579
  data: {
673
- conversion: { // 🤖 AUTOMATIC - Conversion Info
674
- name: string; // Your conversion name
580
+ conversion: {
581
+ name: string; // Your conversion name
675
582
  },
676
- page: { // 🤖 AUTOMATIC - Page Information
677
- url: string; // Full URL
678
- title: string; // Page title
583
+ page: {
584
+ url: string;
585
+ title: string;
679
586
  },
680
- custom?: { // 👤 YOUR DATA (optional)
681
- // All properties from options parameter
682
- value?: number;
683
- currency?: string;
684
- category?: string;
685
- // ... any other custom properties
587
+ custom?: { // Your optional data from options parameter
588
+ // All properties from options
686
589
  }
687
590
  }
688
591
  }]
689
592
  }
690
593
  ```
691
594
 
692
- **Key Points:**
595
+ #### Click Event
693
596
 
694
- - 🤖 Conversion name and page data are captured **automatically**
695
- - 👤 All properties in `options` go into the `custom` object
696
- - ⚠️ **Only track conversions after successful completion of business goals**
597
+ When click tracking is enabled and a user clicks on a contentlet element:
598
+
599
+ ```json
600
+ {
601
+ "content": {
602
+ "identifier": "abc123",
603
+ "inode": "xyz789",
604
+ "title": "Product Page",
605
+ "content_type": "Page"
606
+ },
607
+ "element": {
608
+ "text": "Start Free Trial",
609
+ "type": "a",
610
+ "id": "cta-signup",
611
+ "class": "btn btn-primary",
612
+ "href": "/signup",
613
+ "attributes": [
614
+ "data-category:primary-cta",
615
+ "data-campaign:summer-sale",
616
+ "aria-label:Sign up for free trial"
617
+ ]
618
+ },
619
+ "position": {
620
+ "viewport_offset_pct": 45.2,
621
+ "dom_index": 2
622
+ }
623
+ }
624
+ ```
697
625
 
698
626
  ## Under the Hood
699
627
 
700
628
  ### Storage Keys
701
629
 
702
- - `dot_analytics_user_id`
703
- - `dot_analytics_session_id`
704
- - `dot_analytics_session_utm`
705
- - `dot_analytics_session_start`
630
+ | Key | Purpose |
631
+ | --- | --- |
632
+ | `dot_analytics_user_id` | Anonymous user identifier (persisted across sessions) |
633
+ | `dot_analytics_session_id` | Current session ID |
634
+ | `dot_analytics_session_utm` | UTM campaign data for the session |
635
+ | `dot_analytics_session_start` | Session start timestamp |
706
636
 
707
637
  ### Editor Detection
708
638
 
709
- Analytics are disabled when inside the dotCMS editor.
639
+ Analytics are automatically disabled when inside the dotCMS Universal Visual Editor (UVE). No events are sent in editor mode.
640
+
641
+ ### Endpoint
642
+
643
+ All events are sent via `POST` to:
644
+
645
+ ```
646
+ {server}/api/v1/analytics/content/event
647
+ ```
648
+
649
+ Where `{server}` is the `server` value from your config.
710
650
 
711
651
  ## Debugging & Troubleshooting
712
652
 
713
- **Not seeing events?**
653
+ ### Enable Debug Mode
654
+
655
+ Set `debug: true` in your config to see verbose logging in the browser console.
656
+
657
+ ### Verify Events in the Network Tab
658
+
659
+ 1. Open browser DevTools > Network tab
660
+ 2. Filter by `/api/v1/analytics/content/event`
661
+ 3. Perform actions in your app
662
+ 4. Inspect request payloads to see captured data
663
+
664
+ ### Check Storage
665
+
666
+ Open browser DevTools > Application > Local Storage and look for:
667
+
668
+ - `dot_analytics_user_id`
669
+ - `dot_analytics_session_id`
670
+ - `dot_analytics_session_utm`
671
+ - `dot_analytics_session_start`
672
+
673
+ ### Common Issues
674
+
675
+ **Events not appearing?**
714
676
 
715
- - Ensure `siteKey` & `server` are correct
716
- - Enable debug mode
717
- - Check network requests to: `https://your-server/api/v1/analytics/content/event`
718
- - Avoid using inside dotCMS editor (auto-disabled)
677
+ - Verify `siteAuth` and `server` are correct in your config
678
+ - Enable `debug: true` to see console logs
679
+ - Ensure environment variables are set in `.env.local` (restart dev server after changes)
680
+ - Check that variable names start with `NEXT_PUBLIC_`
681
+ - Analytics are auto-disabled in dotCMS editor mode -- test in preview or published mode
719
682
 
720
- Standalone attributes to verify:
683
+ **Queue not flushing?**
721
684
 
722
- - `data-analytics-auth` (required)
723
- - `data-analytics-server` (optional, defaults to current origin)
724
- - `data-analytics-auto-page-view` (`true` to enable)
725
- - `data-analytics-debug` (`true` to enable)
685
+ - Check `eventBatchSize` -- the threshold might not be reached yet
686
+ - Verify `flushInterval` is appropriate for your use case
687
+ - Events auto-flush on page navigation/close via `visibilitychange`
726
688
 
727
- ## Roadmap
689
+ **Session not persisting?**
728
690
 
729
- - Scroll depth & file download tracking
730
- - Form interaction analytics
731
- - Angular & Vue support
732
- - Realtime dashboard
691
+ - Check that localStorage is enabled in the browser
692
+ - Verify no browser extensions are blocking storage
733
693
 
734
694
  ## Support
735
695
 
736
696
  We offer multiple channels to get help with the dotCMS Analytics SDK:
737
697
 
738
- - **GitHub Issues**: For bug reports and feature requests, please [open an issue](https://github.com/dotCMS/core/issues/new/choose) in the GitHub repository
739
- - **Community Forum**: Join our [community discussions](https://community.dotcms.com/) to ask questions and share solutions
740
- - **Stack Overflow**: Use the tag `dotcms-analytics` when posting questions
741
- - **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://helpdesk.dotcms.com/support/)
698
+ - **GitHub Issues**: For bug reports and feature requests, please [open an issue](https://github.com/dotCMS/core/issues/new/choose) in the GitHub repository.
699
+ - **Community Forum**: Join our [community discussions](https://community.dotcms.com/) to ask questions and share solutions.
700
+ - **Stack Overflow**: Use the tag `dotcms-analytics` when posting questions.
701
+ - **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://helpdesk.dotcms.com/support/).
742
702
 
743
703
  When reporting issues, please include:
744
704