dotCMS logodotCMS logo
OverviewAuthorBuildManageReference
dotCMS Github RepodotCMS Github RepodotCMS Discourse CommunityChat With Us
Headless and SDKs
Build
Getting StartedContent ModelingHeadless and SDKsTemplates and ThemesServer Side ScriptingAPIsExtending dotCMSIntegrationsSearch

SDK Libraries

  • Headless SDK
  • SDK Example Projects
  • SDK: @dotcms/analytics Library
  • SDK: @dotcms/angular Library
  • SDK: @dotcms/client Library
  • SDK: @dotcms/experiments Library
  • SDK: @dotcms/react Library
  • SDK: @dotcms/types Library
  • SDK: @dotcms/uve Library
  • SDK: dotcms/php-sdk Library (alpha)

Example Projects

  • SDK: .NET Example
  • SDK: Angular Example
  • SDK: Astro Example
  • SDK: Laravel Example
  • SDK: Next.js Example
  • SDK: Symfony Example

dotCMS

Universal content management, made simple.

GitHubTwitterLinkedIndotCMS Discourse Community
dotDev HomeBlogChangelogsCurrent releasesAboutContactPrivacy PolicyTrust Center

© 2026 dotCMS. All rights reserved.

    Assistant

    Responses are generated using AI and may contain mistakes.

    Hi, I'm an AI assistant with access to documentation and other content.

    Tip: You can toggle this pane with ⌘/ or ⌘K.

    Try asking

    Enter to send · Shift+Enter for newline

    @dotcms/analytics

    Content 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 config as 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
    VariableDescription
    NEXT_PUBLIC_DOTCMS_ANALYTICS_SITE_KEYSite auth key from the Content Analytics app in dotCMS
    NEXT_PUBLIC_DOTCMS_ANALYTICS_HOSTURL where your dotCMS instance is running
    NEXT_PUBLIC_DOTCMS_ANALYTICS_DEBUGSet to "true" to enable verbose console logging

    Config Options#

    OptionTypeRequiredDefaultDescription
    siteAuthstringYes--Site auth from dotCMS Analytics app
    serverstringYes--Your dotCMS server URL
    debugbooleanNofalseEnable verbose logging
    autoPageViewbooleanNotrueAuto track page views on route changes
    queueQueueConfig | falseNoSee belowEvent batching configuration
    impressionsImpressionConfig | booleanNofalseContent impression tracking
    clicksbooleanNofalseContent 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
    OptionTypeDefaultDescription
    eventBatchSizenumber15Max events per batch -- auto-sends when reached
    flushIntervalnumber5000Time 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
    // 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
    OptionTypeDefaultDescription
    visibilityThresholdnumber0.5Min percentage visible (0.0 to 1.0)
    dwellMsnumber750Min time visible in milliseconds
    maxNodesnumber1000Max 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
    // 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:

    <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 TypeTriggerRequires
    pageviewAutomatically on route change, or manually via pageView()autoPageView: true or manual call
    content_impressionWhen a contentlet becomes visible in the viewportimpressions config enabled
    content_clickWhen a user clicks a link/button inside a contentletclicks config enabled
    conversionExplicitly via conversion() after a successful business actionManual 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.

    // 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#

    KeyPurpose
    dot_analytics_user_idAnonymous user identifier (persisted across sessions)
    dot_analytics_session_idCurrent session ID
    dot_analytics_session_utmUTM campaign data for the session
    dot_analytics_session_startSession 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 in the GitHub repository.
    • Community Forum: Join our community discussions 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.

    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
    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) 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.

    Package: @dotcms/analytics
    Tag: latest

    Found an issue with this documentation? View the source