Limits of Storybook addon-controls with non-serializable props in 6.x and 7.x
0 reputation · 29 Apr 2023, 05:25 UTC
0 reputation · 29 Apr 2023, 05:25 UTC
Storybook's addon-controls panel serializes prop values using JSON.stringify. In Storybook 6.x and 7.x, values that are not JSON-serializable—such as functions, symbols, circular references, or undefined—are replaced by a non-editable placeholder or removed from the controls list entirely. Because the panel does not expose a documented parameter or addon option to substitute a custom serializer, teams cannot override this default behavior through standard configuration.
When a placeholder appears, the control cannot be edited, and invoking the prop from the UI is a no-op. This makes callback props effectively invisible to interactive testing within Storybook, even though the component may rely on them at runtime. The behavior is consistent across recent minor releases because the serialization logic is embedded in the core @storybook/addon-essentials package.
Given these constraints, is there any supported mechanism to configure serialization for non-serializable props in addon-controls? If not, what is the recommended boundary for using controls with function or circular props without misrepresenting component testability?
32775 reputation · 29 Apr 2023, 10:25 UTC
There is no documented or supported mechanism to replace or extend the JSON‑stringify based serialization used by Storybook’s @storybook/addon-controls panel in versions 6.x and 7.x. The serialization logic is hard‑coded in the core controls implementation, so non‑serializable values (functions, symbols, undefined, circular objects, BigInt, etc.) are always replaced by a non‑editable placeholder or omitted from the controls list.
args, it runs JSON.stringify(value) on each prop to produce a displayable representation.JSON.stringify throws or returns undefined for a value, the panel shows a placeholder (e.g., "[Function]", "[Circular]") that cannot be edited, or it simply hides the control.To avoid misrepresenting a component’s testability, keep the controls panel limited to JSON‑serializable args only. For props that are functions, circular objects, or other non‑serializable values:
parameters.controls to disable or filter those props:// .storybook/preview.js (global)
export const parameters = {
controls: {
exclude: ['onClick', 'onChange', 'complexObj'] // prop names to hide
}
};
// or per‑story
export const parameters = {
controls: { exclude: ['onClick'] }
};
args but give it a serializable placeholder (e.g., an object with a name field) and restore the actual function/object in a decorator:// story.js
export const MyStory = {
render: (args) => <MyButton {...args} />,
args: {
onClick: { name: 'onClick' }, // serializable placeholder
label: 'Click me'
},
parameters: {
controls: { include: ['label'] } // only show label in controls
},
decorators:[(Story) => {
const story = Story();
// replace placeholder with the real function
story.props.onClick = () => alert('clicked');
return story;
}]
};
npx sb init).export const FunctionStory = {
render: (args) => <div onClick={args.onClick}>Click</div>,
args: { onClick: () => console.log('clicked'), circular: { a: 1 } }
};
// make circular
FunctionStory.args.circular.self = FunctionStory.args.circular;
npm run storybook and open the Controls panel.onClick control appears as a disabled placeholder (or is missing) and the circular is either omitted or shown as a non‑editable "[Circular]".args or exclude it via parameters.controls.exclude and verify that the remaining controls become editable.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.