SDK: @dotcms/analytics Library
Beta FeatureContent Analytics SDK for tracking content-aware events in dotCMS-powered React applications.
Quick Start#
1. Install#
npm install @dotcms/analytics
2. Create a centralized config file#
// 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#
// 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#
'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
configas a parameter -- it does not read from context - Import the centralized config in every component that uses the hook
// 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:
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 immediatelyundefined(default): Enable queuing with default settingsQueueConfigobject: 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
eventBatchSizeis reached - Sends pending events every
flushIntervalmilliseconds - Auto-flushes on page navigation/close using
visibilitychange+pagehide - Uses
navigator.sendBeacon()for reliable delivery on page unload
// 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:
falseorundefined(default): Disabledtrue: Enabled with default settingsImpressionConfigobject: 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-contentletclass anddata-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
// 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:
falseorundefined(default): Disabledtrue: Enabled with 300ms throttle
How it works:
- Tracks clicks on
<a>and<button>elements within contentlets - Contentlets must be marked with
dotcms-contentletclass anddata-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:
<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.
eventNamecannot 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.
// 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#
'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#
'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:
'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#
'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.
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.
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#
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:
{ 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):
{ 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):
{ 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:
{ "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#
- Open browser DevTools > Network tab
- Filter by
/api/v1/analytics/content/event - Perform actions in your app
- Inspect request payloads to see captured data
Check Storage#
Open browser DevTools > Application > Local Storage and look for:
dot_analytics_user_iddot_analytics_session_iddot_analytics_session_utmdot_analytics_session_start
Common Issues#
Events not appearing?
- Verify
siteAuthandserverare correct in your config - Enable
debug: trueto 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
flushIntervalis 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 in the GitHub repository.
- Community Forum: Join our community discussions to ask questions and share solutions.
- Stack Overflow: Use the tag
dotcms-analyticswhen posting questions. - Enterprise Support: Enterprise customers can access premium support through the dotCMS Support Portal.
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:
- Fork the repository dotCMS/core
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please ensure your code follows the existing style and includes appropriate tests.
Licensing#
dotCMS comes in multiple editions and as such is dual-licensed. The dotCMS Community Edition is licensed under the GPL 3.0 and is freely available for download, customization, and deployment for use within organizations of all stripes. dotCMS Enterprise Editions (EE) adds several enterprise features and is available via a supported, indemnified commercial license from dotCMS. For the differences between the editions, see the feature page.
This SDK is part of dotCMS's dual-licensed platform (GPL 3.0 for Community, commercial license for Enterprise).
Learn more at dotcms.com.
mainFound an issue with this documentation? Report it on GitHub