Skip to content

Commit 0f0ebd0

Browse files
fix(react): resolve structured-clone exception inside React 19 / Storybook (#1310)
1 parent eac0a56 commit 0f0ebd0

1 file changed

Lines changed: 41 additions & 1 deletion

File tree

packages/react/.storybook/preview.js

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,48 @@
11
import 'normalize.css';
22

3+
// ─── React 19 logComponentRender patch ───────────────────────────────────────
4+
// React 19's dev-mode calls performance.measure() with a `detail` option that
5+
// contains component prop diffs. The browser internally structured-clones that
6+
// detail, and crashes with:
7+
// "DOMException: unsupported type for structured data"
8+
// when the EmbeddedChatApi context value (which holds WebSocket/Promises) is
9+
// diffed as a prop on the Context.Provider component.
10+
//
11+
// We can't configure this away — it's baked into react-dom-client.development.js.
12+
// The patch below wraps performance.measure so the DOMException is caught and
13+
// the call is retried without the non-serializable detail, keeping the browser's
14+
// performance timeline intact for everything else.
15+
if (typeof performance !== 'undefined' && typeof performance.measure === 'function') {
16+
const _originalMeasure = performance.measure.bind(performance);
17+
performance.measure = function patchedMeasure(name, startOrOptions, endMark) {
18+
try {
19+
if (typeof startOrOptions === 'object' && startOrOptions !== null) {
20+
return _originalMeasure(name, startOrOptions, endMark);
21+
}
22+
return _originalMeasure(name, startOrOptions, endMark);
23+
} catch (e) {
24+
if (e instanceof DOMException) {
25+
// Retry without the detail so the mark still appears in DevTools
26+
try {
27+
const safe = { ...startOrOptions };
28+
delete safe.detail;
29+
return _originalMeasure(name, safe);
30+
} catch (_) { /* ignore */ }
31+
} else {
32+
throw e;
33+
}
34+
}
35+
};
36+
}
37+
338
/** @type { import('@storybook/react').Preview } */
439
const preview = {
540
parameters: {
6-
actions: { argTypesRegex: '^on[A-Z].*' },
41+
// IMPORTANT: Do NOT re-enable argTypesRegex here.
42+
// Storybook's action addon auto-wraps any on* prop matching the regex and
43+
// tries to structured-clone every argument via postMessage. File/Blob objects
44+
// are not cloneable and would cause the same DOMException.
45+
actions: { disable: true },
746
controls: {
847
matchers: {
948
color: /(background|color)$/i,
@@ -14,3 +53,4 @@ const preview = {
1453
};
1554

1655
export default preview;
56+

0 commit comments

Comments
 (0)