Adopting CSF3 Typed Args and the Controls Addon in Storybook
Replace static Storybook examples with CSF3 typed args and the Controls addon. Learn the prerequisites, authoring steps, URL serialization rules, and recovery from cache or config problems.
12 Sept 2025, 02:22 UTC

Desired Outcome: Interactive, Shareable Component States
Static story examples document a component at one point in time. Component Story Format 3 (CSF3) changes that by making each story a function of typed arguments. Combined with the Controls addon from @storybook/addon-essentials, engineers can tweak props in the browser, share the resulting URL, and reset to defaults. The practical payoff is that stories become live contracts rather than screenshots.
Prerequisites
Before writing CSF3 stories, confirm the following:
- A working Storybook installation for your target framework (React, Vue, Angular, Web Components, etc.). This guide uses React for concrete examples; renderer-specific behavior may vary.
@storybook/addon-essentialsis listed in youraddonsarray in.storybook/main.jsor.storybook/main.ts. The Controls addon ships with Essentials.- NPM scripts (or equivalent) to run the dev server and build static output. In recent Storybook majors these are typically
storybook devandstorybook build, but verify against your installed version. - Record your installed Storybook major from
package.jsonor the CLI version output before running commands. Config file names, CLI flags, and addon APIs have changed across majors.
Authoring a CSF3 Story with Typed Args
Place the story file beside the component, for example src/components/Button/Button.stories.jsx (or .tsx for TypeScript).
// src/components/Button/Button.stories.jsx
import Button from './Button';
export default {
component: Button,
title: 'Components/Button',
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
},
size: {
control: 'radio',
options: ['sm', 'md', 'lg'],
},
disabled: {
control: 'boolean',
},
label: {
control: 'text',
},
},
};
export const Primary = {
args: {
label: 'Click me',
variant: 'primary',
size: 'md',
disabled: false,
},
};
export const DangerLarge = {
args: {
label: 'Delete',
variant: 'danger',
size: 'lg',
disabled: false,
},
};
Key points:
- The default export is the meta object.
componentlinks the story to the implementation;titledetermines the sidebar location;tags: ['autodocs']generates a documentation page from these same stories. - Each named export is a story. The
argsobject seeds the rendered component and becomes the initial state for Controls. argTypesmaps prop names to control widgets. Thecontrolfield accepts values such astext,boolean,number(withmin,max,step),select,radio,color,date,object, andfile. Exact availability depends on your installed Storybook major and renderer.
Keeping Args Serializable
Controls serializes args into the Storybook URL so a tuned state can be bookmarked or shared. This is the engineering reason to keep args JSON-serializable. Do not pass functions, class instances, or React elements as args. Move complex behavior into render functions, decorators, or mocks instead.
Running the Dev Server and Checking Controls
Run the dev server from your project root. You need local file access; no elevated system permissions are required.
npm run storybook dev
Expected checks in the browser:
- The Controls panel shows a widget for each
argTypesentry—text input, select dropdown, radio group, or boolean toggle. - Editing a control re-renders the story immediately.
- A reset action restores the default args defined in the story export.
- The browser console stays free of story-related errors.
- The URL updates as you change args, confirming the state is serialized.
If the Controls panel is empty or missing:
- Verify
@storybook/addon-essentialsis present in theaddonsarray of.storybook/main.jsor.storybook/main.ts. Restart the dev server after any config change. - Confirm the story file uses the CSF3 shape: a default meta export plus named story exports. Older CSF2 formats with
storiesOfor template functions will not populate Controls the same way.
Recovery from Stale UI and Cache Problems
If stories are missing, the UI behaves oddly, or newly added controls do not appear, stale cache is a common cause. Stop the dev server, delete Storybook's cache directory, and restart. The exact path varies by framework and Storybook major; it is commonly located under node_modules/.cache and is typically ignored by version control. Check your project's .gitignore or Storybook's terminal output for the precise location.
# Stop the dev server first (Ctrl+C), then from the project root:
rm -rf node_modules/.cache/storybook
npm run storybook dev
Risk: deleting cache forces a full rebuild on next start, which may take longer on large projects. No source files are modified.
Building Static Output
Before merging, confirm stories compile in a production build. Run from the project root:
npm run storybook build
Expected check: the command exits successfully and emits a static output directory. If the build fails with addon or config errors, re-check the addons list and argTypes syntax against the installed major.
Limitations and Version Verification
- Renderer differences: argTypes inference and decorator behavior differ per framework renderer. The React example above may not transfer directly to Vue, Angular, or Web Components without adjustment.
- Serialization limits: Non-serializable args (functions, class instances) do not work with Controls. Use
renderfunctions or decorators. - API drift: Storybook config file names, CLI flags, and addon APIs changed across major releases. Do not assume
storybook devormain.jsexist in older installations. Verify every command and option against the official documentation for your installed major. - Autodocs scope:
tags: ['autodocs']generates pages from stories, but the exact page structure and available props tables depend on the installed version and TypeScript/renderer support.
Practical Verification
After setup, open a story, change two controls, and copy the URL into a private browser window. If the component renders with the same args, serialization works. Then run the production build once to ensure CI will not fail on config drift.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.