The @dotcms/experiments SDK is the official dotCMS JavaScript library that helps add A/B testing to your web applications. It handles user assignments to different variants of a page and tracks their interactions.
Install the package via npm:
npm install @dotcms/experimentsOr using Yarn:
yarn add @dotcms/experimentsAlign @dotcms/experiments with the same version line as your other @dotcms/* packages (for example ^1.5.6).
The package exports one public symbol:
| Export | Description |
|---|---|
withExperiments | HOC that wraps a layout component (typically DotCMSLayoutBody) with experiment handling |
DotExperimentsProvider and other internals are not part of the public API. Do not import them — withExperiments wires the provider internally.
withExperiments Higher-Order Component#withExperiments wraps your page component with experiment functionality:
Important:
withExperimentsuses React hooks internally. It must be called unconditionally on every render of the component that uses it. See Anti-patterns.
The config object passed to withExperiments accepts the following properties:
| Property | Required | Default | Description |
|---|---|---|---|
apiKey | Yes | — | jsKey from the dotCMS Analytics / Experiments app (frontend key, not m2m) |
server | Yes | — | dotCMS origin URL (e.g. https://your-dotcms-instance.com) — not a CDN |
redirectFn | No | window.location.replace | SPA redirect function (e.g. router.replace in Next.js) |
trackPageView | No | true | Enable/disable automatic page view tracking |
debug | No | false | Verbose logging in the browser console |
Use separate views or routes for pages with and without experiments. This avoids calling withExperiments conditionally in the same component.
// views/StandardPage.tsx — no experiments
"use client";
import { DotCMSLayoutBody, useEditableDotCMSPage } from "@dotcms/react";
import { pageComponents } from "@/components/content-types";
export function StandardPage({ pageContent }) {
const { pageAsset } = useEditableDotCMSPage(pageContent);
return (
<main>
<DotCMSLayoutBody page={pageAsset} components={pageComponents} />
</main>
);
}// views/ExperimentsPage.tsx — experiments always enabled on this view
"use client";
import { withExperiments } from "@dotcms/experiments";
import { DotCMSLayoutBody, useEditableDotCMSPage } from "@dotcms/react";
import { useRouter } from "next/navigation";
import { pageComponents } from "@/components/content-types";
export function ExperimentsPage({ pageContent }) {
const { pageAsset } = useEditableDotCMSPage(pageContent);
const { replace } = useRouter();
const LayoutBody = withExperiments(DotCMSLayoutBody, {
apiKey: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_KEY!,
server: process.env.NEXT_PUBLIC_DOTCMS_HOST!,
redirectFn: replace,
debug: process.env.NEXT_PUBLIC_DOTCMS_DEBUG === "true",
});
return (
<main>
<LayoutBody page={pageAsset} components={pageComponents} />
</main>
);
}Wire each view to its own route in the App Router (for example /blog for experiments, catch-all for standard pages).
Learn more about
DotCMSLayoutBody: See the @dotcms/react SDK documentation.
Canonical working example: examples/nextjs-analytics-experiments
That project demonstrates:
@dotcms/analytics in layout.tsx/blog via withExperiments in a dedicated viewEnvironment variables (see the example's .env.local.example):
NEXT_PUBLIC_DOTCMS_HOST=http://localhost:8080 # dotCMS origin — NOT a CDN
NEXT_PUBLIC_DOTCMS_ANALYTICS_KEY= # jsKey (frontend key)
NEXT_PUBLIC_DOTCMS_DEBUG=trueVerification (DevTools → Network):
POST {HOST}/api/v1/experiments/isUserIncludeddebug: true: [dotCMS ...] logs in the consoleapiKey and serverwithExperiments with a ternary on apiKey in the same componentrouter.replace (Next.js) or equivalent to redirectFn for SPA navigationserver must point to the dotCMS instance origin. Do not use Azure Front Door, CloudFront, or other CDNs — experiment API responses are session-specific and must not be cached at the edge@dotcms/client, @dotcms/react, @dotcms/uve, @dotcms/types, and @dotcms/experiments on the same version lineDo not use these patterns — they can crash Next.js App Router pages and the UVE editor:
// ❌ Conditional withExperiments — violates rules of hooks
const Layout = experimentConfig.apiKey
? withExperiments(DotCMSLayoutBody, config)
: DotCMSLayoutBody;
// ❌ Early return before withExperiments in the same component
if (!apiKey) return <DotCMSLayoutBody {...props} />;
const Layout = withExperiments(DotCMSLayoutBody, config);
// ❌ Importing internal provider
import { DotExperimentsProvider } from "@dotcms/experiments";
// ❌ CDN as server
server: "https://cdn.example.com"Use separate components/routes instead (see With @dotcms/react).
Once you wrap your component with withExperiments, the SDK automatically handles:
Learn More: DotCMS A/B Testing Experiments
| Symptom | Likely cause | What to do |
|---|---|---|
| UVE crash: "A client-side exception has occurred" | withExperiments called conditionally, or experiments initializing before UVE state is ready | Use a dedicated experiments view; never use apiKey ? withExperiments(...) : DotCMSLayoutBody |
GET /api/v1/experiments/DEFAULT 404 | Known backoffice issue — variant name "DEFAULT" passed as experiment ID | Harmless in headless apps; backoffice fix tracked separately |
| "No experiments assigned to the client" | Experiment not in Running status, or no analytics session yet | Confirm experiment status in dotCMS; verify apiKey and server |
No isUserIncluded request | Missing apiKey, wrong route, or experiments view not mounted | Check env vars; confirm the page uses the experiments view |
| Events go to wrong host | server points to CDN instead of dotCMS origin | Set server to the dotCMS instance URL |
dotcms-experimentsWhen reporting issues, include SDK version, framework version, minimal reproduction steps, and expected vs. actual behavior.
Please ensure your code follows the existing style and includes appropriate tests.
Before any pull requests can be accepted, an automated tool will ask you to agree to the dotCMS Contributor's Agreement.
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.
latestFound an issue with this documentation? View the source