SDK: @dotcms/analytics Library
Beta FeatureContent Analytics SDK for tracking content-aware events in dotCMS-powered React applications.
Which SDK Version Should I Use?#
dotCMS SDKs are published in lockstep with dotCMS itself: every @dotcms/* package ships
at the exact same version number as the dotCMS release it was built for (e.g. dotCMS
26.7.14-1 → @dotcms/client@26.7.14-1, @dotcms/react@26.7.14-1, and so on).
Simple rule of thumb: use the SDK version that matches your dotCMS instance's version.
You don't have to upgrade the SDK every time dotCMS releases a new version (or vice versa). Most releases don't change anything the SDKs rely on, so an older SDK usually keeps working fine against a newer dotCMS instance. Occasionally, though, a release does include a real breaking change — and if your SDK is older than that point, it will stop working correctly.
You don't need to track this yourself: your dotCMS instance always knows the oldest SDK version it still supports, and the SDK checks itself against it automatically. If you're using an SDK that's too old, you'll see a clear warning in your console telling you to upgrade.
Recommendation: pin your SDKs to the same version as your dotCMS instance, and only bump them when you upgrade dotCMS — or when the console tells you to.
On an LTS release? LTS releases don't currently get their own matching SDK version. Until that's addressed, use the SDK version published for the closest regular release at or before your LTS version.
Want more background on how dotCMS releases and support windows work? See Release & Support Lifecycle.
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 is available under either the Business Source License 1.1 (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 and 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.
mainFound an issue with this documentation? Report it on GitHub