@dotcms/experiments 1.5.5-next.2253 → 1.5.5-next.2260

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +134 -54
  2. package/index.esm.js +5 -35
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -22,12 +22,16 @@ The `@dotcms/experiments` SDK is the official dotCMS JavaScript library that hel
22
22
  - [Overview](#overview)
23
23
  - [Installation](#installation)
24
24
  - [Getting Started](#getting-started)
25
+ - [Public API](#public-api)
25
26
  - [withExperiments HOC](#withexperiments-higher-order-component)
26
27
  - [Configuration Object](#configuration-object)
27
28
  - [Usage](#usage)
28
- - [With @dotcms/react](#with-dotcmsreact-recommended)
29
+ - [With @dotcms/react (Recommended)](#with-dotcmsreact-recommended)
30
+ - [Next.js App Router](#nextjs-app-router)
29
31
  - [Configuration Best Practices](#configuration-best-practices)
32
+ - [Anti-patterns](#anti-patterns)
30
33
  - [How It Works](#how-it-works)
34
+ - [Troubleshooting](#troubleshooting)
31
35
  - [Support](#support)
32
36
  - [Contributing](#contributing)
33
37
  - [Licensing](#licensing)
@@ -46,11 +50,23 @@ Or using Yarn:
46
50
  yarn add @dotcms/experiments
47
51
  ```
48
52
 
53
+ Align `@dotcms/experiments` with the same version line as your other `@dotcms/*` packages (for example `^1.5.6`).
54
+
49
55
  ## Getting Started
50
56
 
57
+ ### Public API
58
+
59
+ The package exports **one** public symbol:
60
+
61
+ | Export | Description |
62
+ | --- | --- |
63
+ | `withExperiments` | HOC that wraps a layout component (typically `DotCMSLayoutBody`) with experiment handling |
64
+
65
+ `DotExperimentsProvider` and other internals are **not** part of the public API. Do not import them — `withExperiments` wires the provider internally.
66
+
51
67
  ### `withExperiments` Higher-Order Component
52
68
 
53
- The SDK exports a single HOC (Higher-Order Component) called `withExperiments` that wraps your page component with experiment functionality. This component handles:
69
+ `withExperiments` wraps your page component with experiment functionality:
54
70
 
55
71
  - User assignment to experiment variants
56
72
  - Automatic redirection to the correct variant
@@ -58,67 +74,133 @@ The SDK exports a single HOC (Higher-Order Component) called `withExperiments` t
58
74
  - Automatic page view tracking to dotCMS Analytics
59
75
  - Click event handling to maintain variant consistency across navigation
60
76
 
77
+ > **Important:** `withExperiments` uses React hooks internally. It must be called **unconditionally** on every render of the component that uses it. See [Anti-patterns](#anti-patterns).
78
+
61
79
  ### Configuration Object
62
80
 
63
81
  The `config` object passed to `withExperiments` accepts the following properties:
64
82
 
65
- - **apiKey** (required): Your API key from the DotCMS Analytics app
66
- - **server** (required): The URL of your dotCMS instance (e.g., `https://your-dotcms-instance.com`)
67
- - **redirectFn** (optional): Custom redirect function for SPA navigation (default: `window.location.replace`)
68
- - **trackPageView** (optional): Enable/disable automatic page view tracking (default: `true`)
69
- - **debug** (optional): Enable debug logging in the browser console (default: `false`)
83
+ | Property | Required | Default | Description |
84
+ | --- | --- | --- | --- |
85
+ | `apiKey` | Yes | | jsKey from the dotCMS Analytics / Experiments app (frontend key, not m2m) |
86
+ | `server` | Yes | | dotCMS **origin** URL (e.g. `https://your-dotcms-instance.com`) — not a CDN |
87
+ | `redirectFn` | No | `window.location.replace` | SPA redirect function (e.g. `router.replace` in Next.js) |
88
+ | `trackPageView` | No | `true` | Enable/disable automatic page view tracking |
89
+ | `debug` | No | `false` | Verbose logging in the browser console |
70
90
 
71
91
  ## Usage
72
92
 
73
93
  ### With @dotcms/react (Recommended)
74
94
 
75
- The recommended approach is to wrap the `DotCMSLayoutBody` component with `withExperiments`. The pattern supports **conditional wrapping** - experiments are only enabled when an API key is configured:
95
+ Use **separate views or routes** for pages with and without experiments. This avoids calling `withExperiments` conditionally in the same component.
96
+
97
+ ```tsx
98
+ // views/StandardPage.tsx — no experiments
99
+ "use client";
100
+
101
+ import { DotCMSLayoutBody, useEditableDotCMSPage } from "@dotcms/react";
102
+
103
+ import { pageComponents } from "@/components/content-types";
76
104
 
77
- ```javascript
105
+ export function StandardPage({ pageContent }) {
106
+ const { pageAsset } = useEditableDotCMSPage(pageContent);
107
+
108
+ return (
109
+ <main>
110
+ <DotCMSLayoutBody page={pageAsset} components={pageComponents} />
111
+ </main>
112
+ );
113
+ }
114
+ ```
115
+
116
+ ```tsx
117
+ // views/ExperimentsPage.tsx — experiments always enabled on this view
78
118
  "use client";
79
119
 
80
120
  import { withExperiments } from "@dotcms/experiments";
81
121
  import { DotCMSLayoutBody, useEditableDotCMSPage } from "@dotcms/react";
82
- import { useRouter } from 'next/navigation';
122
+ import { useRouter } from "next/navigation";
83
123
 
84
- // Experiment configuration - only applied if apiKey is present
85
- const experimentConfig = {
86
- apiKey: process.env.NEXT_PUBLIC_EXPERIMENTS_API_KEY,
87
- server: process.env.NEXT_PUBLIC_DOTCMS_HOST,
88
- debug: true
89
- };
124
+ import { pageComponents } from "@/components/content-types";
90
125
 
91
- export function Page({ pageContent }) {
126
+ export function ExperimentsPage({ pageContent }) {
92
127
  const { pageAsset } = useEditableDotCMSPage(pageContent);
93
128
  const { replace } = useRouter();
94
129
 
95
- // Conditionally wrap with experiments if apiKey is configured
96
- const DotCMSLayoutBodyComponent = experimentConfig.apiKey
97
- ? withExperiments(DotCMSLayoutBody, {
98
- ...experimentConfig,
99
- redirectFn: replace
100
- })
101
- : DotCMSLayoutBody;
130
+ const LayoutBody = withExperiments(DotCMSLayoutBody, {
131
+ apiKey: process.env.NEXT_PUBLIC_DOTCMS_ANALYTICS_KEY!,
132
+ server: process.env.NEXT_PUBLIC_DOTCMS_HOST!,
133
+ redirectFn: replace,
134
+ debug: process.env.NEXT_PUBLIC_DOTCMS_DEBUG === "true",
135
+ });
102
136
 
103
137
  return (
104
138
  <main>
105
- <DotCMSLayoutBodyComponent
106
- page={pageAsset}
107
- components={pageComponents}
108
- />
139
+ <LayoutBody page={pageAsset} components={pageComponents} />
109
140
  </main>
110
141
  );
111
142
  }
112
143
  ```
113
144
 
114
- > 📚 **Learn more about `DotCMSLayoutBody`**: See the [@dotcms/react SDK documentation](https://github.com/dotCMS/core/blob/main/core-web/libs/sdk/react/README.md#dotcmslayoutbody) for complete details on configuring the layout renderer, component mapping, and available props.
145
+ Wire each view to its own route in the App Router (for example `/blog` for experiments, catch-all for standard pages).
146
+
147
+ > **Learn more about `DotCMSLayoutBody`**: See the [@dotcms/react SDK documentation](https://github.com/dotCMS/core/blob/main/core-web/libs/sdk/react/README.md#dotcmslayoutbody).
148
+
149
+ ### Next.js App Router
150
+
151
+ Canonical working example: [`examples/nextjs-analytics-experiments`](../../../../examples/nextjs-analytics-experiments)
152
+
153
+ That project demonstrates:
154
+
155
+ - Analytics on all routes via `@dotcms/analytics` in `layout.tsx`
156
+ - Experiments isolated to `/blog` via `withExperiments` in a dedicated view
157
+ - Safe separation that avoids rules-of-hooks violations in UVE
158
+
159
+ **Environment variables** (see the example's `.env.local.example`):
160
+
161
+ ```bash
162
+ NEXT_PUBLIC_DOTCMS_HOST=http://localhost:8080 # dotCMS origin — NOT a CDN
163
+ NEXT_PUBLIC_DOTCMS_ANALYTICS_KEY= # jsKey (frontend key)
164
+ NEXT_PUBLIC_DOTCMS_DEBUG=true
165
+ ```
166
+
167
+ **Verification** (DevTools → Network):
168
+
169
+ 1. On experiment routes: `POST {HOST}/api/v1/experiments/isUserIncluded`
170
+ 2. With `debug: true`: `[dotCMS ...]` logs in the console
171
+ 3. Open the page in UVE — no "client-side exception" overlay
115
172
 
116
173
  ### Configuration Best Practices
117
174
 
118
- - **Use Environment Variables**: Store your API key and server URL in environment variables
119
- - **Conditional Wrapping**: Only enable experiments when an API key is configured using a ternary operator
120
- - **Framework Router**: Pass your framework's router function (e.g., `router.replace` for Next.js) to maintain SPA navigation
121
- - **Debug Mode**: Enable debug logging during development to troubleshoot experiment assignment issues
175
+ - **Use environment variables** for `apiKey` and `server`
176
+ - **Dedicated routes/views** for experiment pages do not toggle `withExperiments` with a ternary on `apiKey` in the same component
177
+ - **Framework router**: pass `router.replace` (Next.js) or equivalent to `redirectFn` for SPA navigation
178
+ - **Origin URL**: `server` 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
179
+ - **Aligned SDK versions**: keep `@dotcms/client`, `@dotcms/react`, `@dotcms/uve`, `@dotcms/types`, and `@dotcms/experiments` on the same version line
180
+ - **Debug mode**: enable during development to troubleshoot assignment issues
181
+
182
+ ### Anti-patterns
183
+
184
+ Do **not** use these patterns — they can crash Next.js App Router pages and the UVE editor:
185
+
186
+ ```tsx
187
+ // ❌ Conditional withExperiments — violates rules of hooks
188
+ const Layout = experimentConfig.apiKey
189
+ ? withExperiments(DotCMSLayoutBody, config)
190
+ : DotCMSLayoutBody;
191
+
192
+ // ❌ Early return before withExperiments in the same component
193
+ if (!apiKey) return <DotCMSLayoutBody {...props} />;
194
+ const Layout = withExperiments(DotCMSLayoutBody, config);
195
+
196
+ // ❌ Importing internal provider
197
+ import { DotExperimentsProvider } from "@dotcms/experiments";
198
+
199
+ // ❌ CDN as server
200
+ server: "https://cdn.example.com"
201
+ ```
202
+
203
+ Use separate components/routes instead (see [With @dotcms/react](#with-dotcmsreact-recommended)).
122
204
 
123
205
  ### How It Works
124
206
 
@@ -130,35 +212,33 @@ Once you wrap your component with `withExperiments`, the SDK automatically handl
130
212
  4. **Navigation Handling**: Maintains variant consistency when users click links
131
213
  5. **Analytics Tracking**: Sends pageview events to DotCMS Analytics automatically
132
214
 
133
- All of this happens behind the scenes - you just need to wrap your component and provide the configuration.
134
-
135
- **Learn More**: For detailed information on creating and managing experiments in dotCMS, visit the [DotCMS A/B Testing Experiments](https://www.dotcms.com/product/ab-testing-experiments) page.
215
+ **Learn More**: [DotCMS A/B Testing Experiments](https://www.dotcms.com/product/ab-testing-experiments)
136
216
 
137
- ## Support
217
+ ## Troubleshooting
138
218
 
139
- We offer multiple channels to get help with the dotCMS Experiments SDK:
219
+ | Symptom | Likely cause | What to do |
220
+ | --- | --- | --- |
221
+ | 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` |
222
+ | `GET /api/v1/experiments/DEFAULT` 404 | Known backoffice issue — variant name `"DEFAULT"` passed as experiment ID | Harmless in headless apps; backoffice fix tracked separately |
223
+ | "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` |
224
+ | No `isUserIncluded` request | Missing `apiKey`, wrong route, or experiments view not mounted | Check env vars; confirm the page uses the experiments view |
225
+ | Events go to wrong host | `server` points to CDN instead of dotCMS origin | Set `server` to the dotCMS instance URL |
140
226
 
141
- - **GitHub Issues**: For bug reports and feature requests, please [open an issue](https://github.com/dotCMS/core/issues/new/choose) in the GitHub repository
142
- - **Community Forum**: Join our [community discussions](https://community.dotcms.com/) to ask questions and share solutions
143
- - **Stack Overflow**: Use the tag `dotcms-experiments` when posting questions
144
- - **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://www.dotcms.com/support)
227
+ ## Support
145
228
 
146
- When reporting issues, please include:
229
+ - **GitHub Issues**: [open an issue](https://github.com/dotCMS/core/issues/new/choose)
230
+ - **Community Forum**: [community.dotcms.com](https://community.dotcms.com/)
231
+ - **Stack Overflow**: tag `dotcms-experiments`
232
+ - **Enterprise Support**: [dotCMS Support Portal](https://helpdesk.dotcms.com/support/)
147
233
 
148
- - SDK version you're using
149
- - Framework/library version (if applicable)
150
- - Minimal reproduction steps
151
- - Expected vs. actual behavior
234
+ When reporting issues, include SDK version, framework version, minimal reproduction steps, and expected vs. actual behavior.
152
235
 
153
236
  ## Contributing
154
237
 
155
- GitHub pull requests are the preferred method to contribute code to dotCMS. We welcome contributions to the dotCMS Experiments SDK! If you'd like to contribute, please follow these steps:
156
-
157
- 1. Fork the repository [dotCMS/core](https://github.com/dotCMS/core)
158
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
159
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
160
- 4. Push to the branch (`git push origin feature/amazing-feature`)
161
- 5. Open a Pull Request
238
+ 1. Fork [dotCMS/core](https://github.com/dotCMS/core)
239
+ 2. Create a feature branch
240
+ 3. Commit your changes
241
+ 4. Open a Pull Request
162
242
 
163
243
  Please ensure your code follows the existing style and includes appropriate tests.
164
244
 
package/index.esm.js CHANGED
@@ -7394,43 +7394,13 @@ const useExperiments = instance => {
7394
7394
  };
7395
7395
 
7396
7396
  /**
7397
- * `DotExperimentsProvider` is a component that uses React's Context API to provide
7398
- * an instance of `DotExperiments` to all of its descendants.
7397
+ * Internal React context provider used by `withExperiments`.
7399
7398
  *
7399
+ * @internal Do not import or use directly — use `withExperiments` from the package entry point.
7400
7400
  *
7401
- * @component
7402
- * @example
7403
- * ```jsx
7404
- *
7405
- * // Your application component
7406
- * function App() {
7407
- *
7408
- * // Configuration options could be taken from environment variables or can send you own.
7409
- * const experimentConfig = {
7410
- * apiKey: process.env.NEXT_PUBLIC_EXPERIMENTS_API_KEY ,
7411
- * server: process.env.NEXT_PUBLIC_DOTCMS_HOST ,
7412
- * debug: process.env.NEXT_PUBLIC_EXPERIMENTS_DEBUG,
7413
- * redirectFn: YourRedirectFunction
7414
- * };
7415
- *
7416
- * return (
7417
- * <DotExperimentsProvider config={experimentConfig}>
7418
- * <Header>
7419
- * <Navigation items={nav} />
7420
- * </Header>
7421
- * <DotcmsLayout entity={{...}} config={{...}} />
7422
- * </DotExperimentsProvider>
7423
- * );
7424
- * }
7425
- * ```
7426
- *
7427
- * @param {object} props - The properties that define the `DotExperimentsProvider`.
7428
- * @param {ReactNode} props.children - The descendants of this provider, which will
7429
- * have access to the provided `DotExperiments` instance.
7430
- * @param {DotExperimentConfig} props.config - The configuration object for `DotExperiments`.
7431
- *
7432
- * @returns {ReactElement} The provider component, which should wrap the components
7433
- * that need access to the `DotExperiments` instance.
7401
+ * @param props.children - Descendants that need access to the `DotExperiments` instance.
7402
+ * @param props.config - Configuration object for `DotExperiments`.
7403
+ * @returns The provider component.
7434
7404
  */
7435
7405
  const DotExperimentsProvider = ({
7436
7406
  children,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotcms/experiments",
3
- "version": "1.5.5-next.2253",
3
+ "version": "1.5.5-next.2260",
4
4
  "description": "Official JavaScript library to use Experiments with DotCMS.",
5
5
  "repository": {
6
6
  "type": "git",