@dotcms/experiments 1.5.5-next.2245 → 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.
- package/README.md +134 -54
- package/index.esm.js +5 -35
- 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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
122
|
+
import { useRouter } from "next/navigation";
|
|
83
123
|
|
|
84
|
-
|
|
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
|
|
126
|
+
export function ExperimentsPage({ pageContent }) {
|
|
92
127
|
const { pageAsset } = useEditableDotCMSPage(pageContent);
|
|
93
128
|
const { replace } = useRouter();
|
|
94
129
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
|
119
|
-
- **
|
|
120
|
-
- **Framework
|
|
121
|
-
- **
|
|
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
|
-
|
|
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
|
-
##
|
|
217
|
+
## Troubleshooting
|
|
138
218
|
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
7402
|
-
* @
|
|
7403
|
-
*
|
|
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,
|