Setup: Headless

This guide covers sites whose pages are rendered by a separate front-end application. If your front end is rendered by dotCMS templates and layouts, see Traditional setup instead.

In headless mode there is no script injection. You activate analytics by installing the SDK and initializing it with the Site Auth for that domain.

The Content Analytics app still has to be configured on the site — that is where your Site Auth comes from, and events sent without a matching one are rejected. What does not carry over is everything below it: the Server-Rendered Pages Configuration toggles and the Advanced Configuration field have no effect here. All tracking behaviour is set in your application code.

Install and Configure#


npm install @dotcms/analytics
// 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
};
OptionRequiredMeaningDefault
siteAuthYesSite Auth from the Content Analytics app, for this domain.
serverYesURL of the dotCMS instance that receives events.
autoPageViewSee the note below — behavior differs by integration.false (standalone)
debugLog analytics activity to the console.false
impressionsTrack content impressions. true, or an object to tune thresholds.disabled
clicksTrack clicks on links and buttons inside content items.disabled
queueEvent batching. An object to override, or false to send immediately.15 events / 5000 ms

React and Next.js#


Place the component once in your root layout:

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>
  );
}

Use the useContentAnalytics hook to send events from any component. It exposes pageView(customData?), track(eventName, properties?) and conversion(name).

const { track, pageView, conversion } = useContentAnalytics(analyticsConfig);

const onPurchase = async () => {
  const ok = await completePurchase(product);
  if (ok) conversion('purchase');   // only AFTER it succeeds
};

Making Content Trackable#


If you render content your own way, add them yourself — clicks and impressions only fire inside an element that carries them:

<div className="dotcms-contentlet"
     data-dot-identifier={content.identifier}
     data-dot-inode={content.inode}
     data-dot-title={content.title}
     data-dot-type={content.contentType}
     data-dot-basetype={content.baseType}>
  {/* <a> and <button> elements inside are tracked when clicks are enabled */}
</div>

Cross-Origin Requests#


In a headless setup your app and your dotCMS instance are usually on different origins — for example an app on www.example.com sending events to example-headless.dotcms.dev. Because the SDK posts JSON, the browser issues a CORS preflight (OPTIONS) before each batch, so your dotCMS instance must allow the app's origin, the POST method and the Content-Type header.

This means every batch costs two round trips rather than one — another reason to leave batching on rather than sending each event immediately.

Standalone Script: Unvalidated#


For non-React apps, load the script directly. It is framework-agnostic JavaScript and exposes window.dotAnalytics, but it has not been tested outside Next.js. Validate event delivery in your app before relying on it.

AttributeMeaningDefault
data-analytics-authRequired. Site Auth for this domain.
data-analytics-serverdotCMS instance URL that receives events.current origin
data-analytics-auto-page-viewAuto-track page views.false
data-analytics-impressionsTrack content impressions.false
data-analytics-clicksTrack content clicks.false
data-analytics-debugConsole debug logging.false
data-analytics-configAdvanced JSON config (queue, impression thresholds).