UNPKG

@dotcms/analytics

Version:

Official JavaScript library for Content Analytics with DotCMS.

729 lines (553 loc) 22.2 kB
# @dotcms/analytics Content Analytics SDK for tracking content-aware events in dotCMS-powered React applications. ## Quick Start ### 1. Install ```bash npm install @dotcms/analytics ``` ### 2. Create a centralized config file ```javascript // src/config/analytics.config.js export const analyticsConfig = { siteAuth: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY, server: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST, autoPageView: true, debug: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_DEBUG === 'true', impressions: true, clicks: true }; ``` ### 3. Add automatic page view tracking to your layout ```jsx // src/app/layout.js import { DotContentAnalytics } from '@dotcms/analytics/react'; import { analyticsConfig } from '@/config/analytics.config'; export default function RootLayout({ children }) { return ( <html lang="en"> <body> <DotContentAnalytics config={analyticsConfig} /> {children} </body> </html> ); } ``` ### 4. Track events in your components ```jsx 'use client'; import { useContentAnalytics } from '@dotcms/analytics/react'; import { analyticsConfig } from '@/config/analytics.config'; function ContactForm() { const { conversion } = useContentAnalytics(analyticsConfig); const handleSubmit = (e) => { e.preventDefault(); // ... submit form logic ... // Track conversion ONLY after successful submission conversion('form-submit', { formName: 'contact-us', formType: 'lead-gen' }); }; return <form onSubmit={handleSubmit}>{/* form fields */}</form>; } ``` ## Understanding the Components The SDK exports two React primitives. Understanding their roles is critical for correct usage. ### `<DotContentAnalytics />` -- Automatic Page View Tracker - Its **only purpose** is to automatically track page views on route changes - It is **NOT** a React Context Provider - It does **NOT** share config with child components - Place it once in your root layout ### `useContentAnalytics(config)` -- Manual Tracking Hook - Used for custom events, conversions, and manual page views - **ALWAYS requires `config` as a parameter** -- it does not read from context - Import the centralized config in every component that uses the hook ```jsx // Every component that tracks events must import config explicitly import { useContentAnalytics } from '@dotcms/analytics/react'; import { analyticsConfig } from '@/config/analytics.config'; const { track, pageView, conversion } = useContentAnalytics(analyticsConfig); ``` > **Why centralize config?** You must import it in each component, but having a single file prevents duplication and makes updates easier. ## Configuration ### Environment Variables Add these to your `.env.local` file: ```bash NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY=YOUR_ANALYTICS_SITE_KEY NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST=http://localhost:8080 NEXT_PUBLIC_DOTCMS_ANALYTICS_DEBUG=true ``` | Variable | Description | | --- | --- | | `NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY` | Site auth key from the Content Analytics app in dotCMS | | `NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST` | URL where your dotCMS instance is running | | `NEXT_PUBLIC_DOTCMS_ANALYTICS_DEBUG` | Set to `"true"` to enable verbose console logging | ### Config Options | Option | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `siteAuth` | `string` | Yes | -- | Site auth from dotCMS Analytics app | | `server` | `string` | Yes | -- | Your dotCMS server URL | | `debug` | `boolean` | No | `false` | Enable verbose logging | | `autoPageView` | `boolean` | No | `true` | Auto track page views on route changes | | `queue` | `QueueConfig \| false` | No | See below | Event batching configuration | | `impressions` | `ImpressionConfig \| boolean` | No | `false` | Content impression tracking | | `clicks` | `boolean` | No | `false` | Content click tracking (300ms throttle) | ### Queue Configuration Controls how events are batched before being sent to the server: - **`false`**: Disable queuing, send events immediately - **`undefined` (default)**: Enable queuing with default settings - **`QueueConfig` object**: Custom settings | Option | Type | Default | Description | | --- | --- | --- | --- | | `eventBatchSize` | `number` | `15` | Max events per batch -- auto-sends when reached | | `flushInterval` | `number` | `5000` | Time between flushes in milliseconds | How it works: - Sends immediately when `eventBatchSize` is reached - Sends pending events every `flushInterval` milliseconds - Auto-flushes on page navigation/close using `visibilitychange` + `pagehide` - Uses `navigator.sendBeacon()` for reliable delivery on page unload ```javascript // Disable queuing (send immediately) export const analyticsConfig = { siteAuth: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY, server: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST, queue: false }; // Custom queue settings export const analyticsConfig = { siteAuth: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEY, server: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_HOST, queue: { eventBatchSize: 10, flushInterval: 3000 } }; ``` ### Impression Tracking Controls automatic tracking of content visibility in the viewport: - **`false` or `undefined` (default)**: Disabled - **`true`**: Enabled with default settings - **`ImpressionConfig` object**: Custom settings | Option | Type | Default | Description | | --- | --- | --- | --- | | `visibilityThreshold` | `number` | `0.5` | Min percentage visible (0.0 to 1.0) | | `dwellMs` | `number` | `750` | Min time visible in milliseconds | | `maxNodes` | `number` | `1000` | Max elements to track (performance limit) | How it works: - Tracks contentlets marked with `dotcms-contentlet` class and `data-dot-*` attributes - Uses Intersection Observer API for performance - Only fires when an element is visible for the configured threshold and dwell time - One impression per contentlet per session (no duplicates) - Automatically disabled in dotCMS editor mode ```javascript // Enable with defaults (50% visible, 750ms dwell) export const analyticsConfig = { // ...required fields impressions: true }; // Custom thresholds export const analyticsConfig = { // ...required fields impressions: { visibilityThreshold: 0.7, dwellMs: 1000, maxNodes: 500 } }; ``` ### Click Tracking Controls automatic tracking of user clicks on content elements: - **`false` or `undefined` (default)**: Disabled - **`true`**: Enabled with 300ms throttle How it works: - Tracks clicks on `<a>` and `<button>` elements within contentlets - Contentlets must be marked with `dotcms-contentlet` class and `data-dot-*` attributes - Captures semantic attributes (`href`, `aria-label`, `data-*`) and excludes CSS classes - Throttles rapid clicks to prevent duplicates (300ms) - Automatically disabled in dotCMS editor mode **Captured data per click:** - **Content Info**: `identifier`, `inode`, `title`, `content_type` - **Element Info**: `text`, `type` (a/button), `id`, `class`, `href`, `attributes` - **Position Info**: `viewport_offset_pct`, `dom_index` You can enrich click data using `data-*` attributes in your HTML: ```html <a href="/signup" id="cta-signup" data-category="primary-cta" data-campaign="summer-sale" aria-label="Sign up for free trial"> Start Free Trial </a> <button data-action="download" data-file-type="pdf" data-category="lead-magnet"> Download Whitepaper </button> ``` ## Core Concepts ### Event Types The SDK sends four types of events, identified by `event_type`: | Event Type | Trigger | Requires | | --- | --- | --- | | `pageview` | Automatically on route change, or manually via `pageView()` | `autoPageView: true` or manual call | | `content_impression` | When a contentlet becomes visible in the viewport | `impressions` config enabled | | `content_click` | When a user clicks a link/button inside a contentlet | `clicks` config enabled | | `conversion` | Explicitly via `conversion()` after a successful business action | Manual call | ### Page Views The `pageView()` method tracks page navigation events. It **automatically enriches** the event with: - **Page data**: URL, title, referrer, path, protocol, search params - **Device data**: Screen resolution, viewport size, language, user agent - **UTM parameters**: Campaign tracking data (source, medium, campaign, term, content) - **Context**: Site key, session ID, user ID, timestamp You can optionally pass custom data that will be sent **in addition** to all the automatic enrichment. ### Conversion Tracking The `conversion()` method tracks user conversions (purchases, downloads, sign-ups, etc.). **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: - Purchase is completed and payment is confirmed - Download is successfully completed - Sign-up form is submitted and account is created - Any business goal is actually achieved ### Custom Events The `track()` method tracks any custom user action with a unique event name and optional properties. - `eventName` cannot be `"pageview"` or `"conversion"` (reserved) - Use descriptive names: `"button-click"`, `"form-submit"`, `"video-play"`, etc. ### Sessions - 30-minute inactivity timeout - Resets at midnight UTC - New session if UTM campaign changes ### Identity - Anonymous user ID persisted across sessions - Stored in `dot_analytics_user_id` ## Usage Examples ### Automatic Page View Tracking Add `DotContentAnalytics` to your root layout. No additional code is needed -- page views are tracked on every route change. ```jsx // src/app/layout.js import { DotContentAnalytics } from '@dotcms/analytics/react'; import { analyticsConfig } from '@/config/analytics.config'; export default function RootLayout({ children }) { return ( <html lang="en"> <body> <DotContentAnalytics config={analyticsConfig} /> {children} </body> </html> ); } ``` ### Manual Page View with Custom Data ```jsx 'use client'; import { useEffect } from 'react'; import { useContentAnalytics } from '@dotcms/analytics/react'; import { analyticsConfig } from '@/config/analytics.config'; function BlogPost({ post }) { const { pageView } = useContentAnalytics(analyticsConfig); useEffect(() => { pageView({ contentType: 'blog', category: post.category, author: post.author, wordCount: post.wordCount }); }, []); return <article>{/* post content */}</article>; } ``` ### Custom Event Tracking ```jsx 'use client'; import { useContentAnalytics } from '@dotcms/analytics/react'; import { analyticsConfig } from '@/config/analytics.config'; function CallToAction() { const { track } = useContentAnalytics(analyticsConfig); const handleClick = () => { track('cta-click', { button: 'Buy Now', location: 'hero-section', price: 299.99 }); }; return <button onClick={handleClick}>Buy Now</button>; } ``` ### Conversion Tracking (Real-World Example) This example is based on the Contact Us form in the [Next.js example app](https://github.com/dotCMS/core/tree/main/examples/nextjs): ```jsx 'use client'; import { useState } from 'react'; import { useContentAnalytics } from '@dotcms/analytics/react'; import { analyticsConfig } from '@/config/analytics.config'; export default function ContactUs({ description }) { const [isSubmitting, setIsSubmitting] = useState(false); const [isSuccess, setIsSuccess] = useState(false); const { conversion } = useContentAnalytics(analyticsConfig); const handleSubmit = (e) => { e.preventDefault(); setIsSubmitting(true); // Simulate form submission setTimeout(() => { setIsSuccess(true); // Track conversion ONLY after successful submission conversion('form-submit', { formName: 'contact-us', formType: 'lead-gen' }); }, 3000); }; return ( <form onSubmit={handleSubmit}> {/* form fields */} <button type="submit" disabled={isSubmitting}> {isSubmitting ? 'Submitting...' : 'Submit'} </button> </form> ); } ``` ### E-commerce Purchase Conversion ```jsx 'use client'; import { useContentAnalytics } from '@dotcms/analytics/react'; import { analyticsConfig } from '@/config/analytics.config'; function CheckoutButton({ product, quantity }) { const { conversion } = useContentAnalytics(analyticsConfig); const handlePurchase = async () => { // Process payment... const result = await processPayment(product, quantity); if (result.success) { // Track conversion ONLY after confirmed payment conversion('purchase', { value: product.price * quantity, currency: 'USD', productId: product.sku, category: product.category }); } }; return <button onClick={handlePurchase}>Complete Purchase</button>; } ``` ## API Reference ### `<DotContentAnalytics />` React component for automatic page view tracking on route changes. ```typescript interface DotContentAnalyticsProps { config: DotCMSAnalyticsConfig; } ``` Place once in your root layout. Wraps the internal tracker in `<Suspense>` for Next.js App Router compatibility. ### `useContentAnalytics(config)` React hook that returns tracking methods. **Always requires config as a parameter.** ```typescript function useContentAnalytics(config: DotCMSAnalyticsConfig): DotCMSAnalytics; interface DotCMSAnalytics { /** Track a page view with optional custom data */ pageView: (customData?: Record<string, unknown>) => void; /** Track a custom event (eventName cannot be "pageview" or "conversion") */ track: (eventName: string, properties?: Record<string, unknown>) => void; /** Track a conversion after a successful business action */ conversion: (name: string, options?: Record<string, unknown>) => void; } ``` ### `DotCMSAnalyticsConfig` ```typescript interface DotCMSAnalyticsConfig { server: string; siteAuth: string; debug?: boolean; autoPageView?: boolean; queue?: QueueConfig | false; impressions?: ImpressionConfig | boolean; clicks?: boolean; } interface QueueConfig { eventBatchSize?: number; flushInterval?: number; } interface ImpressionConfig { visibilityThreshold?: number; dwellMs?: number; maxNodes?: number; } ``` ### Event Payload Structures #### Page View Event When you call `pageView(customData?)`, the SDK sends: ```typescript { context: { site_key: string; // Your site key session_id: string; // Current session ID user_id: string; // Anonymous user ID device: { screen_resolution: string; language: string; viewport_width: string; viewport_height: string; } }, events: [{ event_type: "pageview", local_time: string, // ISO 8601 timestamp with timezone data: { page: { // Captured automatically url: string; title: string; referrer: string; path: string; doc_host: string; doc_protocol: string; doc_search: string; doc_hash: string; doc_encoding: string; }, utm?: { // Captured automatically (if present in URL) source: string; medium: string; campaign: string; term: string; content: string; }, custom?: { // Your optional data from pageView(customData) // Any properties you pass } } }] } ``` #### Custom Event When you call `track(eventName, properties)`: ```typescript { context: { /* same as above */ }, events: [{ event_type: string, // Your custom event name local_time: string, data: { custom: { // Your properties object } } }] } ``` #### Conversion Event When you call `conversion(name, options)`: ```typescript { context: { /* same as above */ }, events: [{ event_type: "conversion", local_time: string, data: { conversion: { name: string; // Your conversion name }, page: { url: string; title: string; }, custom?: { // Your optional data from options parameter // All properties from options } } }] } ``` #### Click Event When click tracking is enabled and a user clicks on a contentlet element: ```json { "content": { "identifier": "abc123", "inode": "xyz789", "title": "Product Page", "content_type": "Page" }, "element": { "text": "Start Free Trial", "type": "a", "id": "cta-signup", "class": "btn btn-primary", "href": "/signup", "attributes": [ "data-category:primary-cta", "data-campaign:summer-sale", "aria-label:Sign up for free trial" ] }, "position": { "viewport_offset_pct": 45.2, "dom_index": 2 } } ``` ## Under the Hood ### Storage Keys | Key | Purpose | | --- | --- | | `dot_analytics_user_id` | Anonymous user identifier (persisted across sessions) | | `dot_analytics_session_id` | Current session ID | | `dot_analytics_session_utm` | UTM campaign data for the session | | `dot_analytics_session_start` | Session start timestamp | ### Editor Detection Analytics are automatically disabled when inside the dotCMS Universal Visual Editor (UVE). No events are sent in editor mode. ### Endpoint All events are sent via `POST` to: ``` {server}/api/v1/analytics/content/event ``` Where `{server}` is the `server` value from your config. ## Debugging & Troubleshooting ### Enable Debug Mode Set `debug: true` in your config to see verbose logging in the browser console. ### Verify Events in the Network Tab 1. Open browser DevTools > Network tab 2. Filter by `/api/v1/analytics/content/event` 3. Perform actions in your app 4. Inspect request payloads to see captured data ### Check Storage Open browser DevTools > Application > Local Storage and look for: - `dot_analytics_user_id` - `dot_analytics_session_id` - `dot_analytics_session_utm` - `dot_analytics_session_start` ### Common Issues **Events not appearing?** - Verify `siteAuth` and `server` are correct in your config - Enable `debug: true` to see console logs - Ensure environment variables are set in `.env.local` (restart dev server after changes) - Check that variable names start with `NEXT_PUBLIC_` - Analytics are auto-disabled in dotCMS editor mode -- test in preview or published mode **Queue not flushing?** - Check `eventBatchSize` -- the threshold might not be reached yet - Verify `flushInterval` is appropriate for your use case - Events auto-flush on page navigation/close via `visibilitychange` **Session not persisting?** - Check that localStorage is enabled in the browser - Verify no browser extensions are blocking storage ## Support We offer multiple channels to get help with the dotCMS Analytics SDK: - **GitHub Issues**: For bug reports and feature requests, please [open an issue](https://github.com/dotCMS/core/issues/new/choose) in the GitHub repository. - **Community Forum**: Join our [community discussions](https://community.dotcms.com/) to ask questions and share solutions. - **Stack Overflow**: Use the tag `dotcms-analytics` when posting questions. - **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://helpdesk.dotcms.com/support/). When reporting issues, please include: - SDK version you're using - Framework/library version (if applicable) - Minimal reproduction steps - Expected vs. actual behavior ## Contributing GitHub pull requests are the preferred method to contribute code to dotCMS. We welcome contributions to the dotCMS Analytics SDK! If you'd like to contribute, please follow these steps: 1. Fork the repository [dotCMS/core](https://github.com/dotCMS/core) 2. Create a feature branch (`git checkout -b feature/amazing-feature`) 3. Commit your changes (`git commit -m 'Add some amazing feature'`) 4. Push to the branch (`git push origin feature/amazing-feature`) 5. Open a Pull Request Please ensure your code follows the existing style and includes appropriate tests. ## Licensing dotCMS is available under either the [Business Source License 1.1 (BSL)](https://www.dotcms.com/bsl) or a commercial license. Under the BSL, dotCMS can be used at no cost by individual developers, small businesses or agencies under $5M in total finances, and by larger organizations in non-production environments. Every BSL release automatically converts to GPL v3 four years after its release date. For full terms and FAQs, visit [dotcms.com/bsl](https://www.dotcms.com/bsl) and [dotcms.com/bsl-faq](https://www.dotcms.com/bsl-faq). Production use in larger organizations, along with access to managed cloud, SLAs, support, and enterprise capabilities, is available under a commercial license from dotCMS. For details on commercial plans, features, and support options, see [dotcms.com/pricing](https://www.dotcms.com/pricing).