Why Storybook Needs Story-Writing Rules
Storybook is the standard tool for developing, documenting, and testing UI components in isolation. But AI assistants generate stories from the pre-CSF3 era: no args (hardcoded props), no play functions (no interaction testing), no controls (no interactive prop editing in the UI), and stories that render a component with static data instead of exercising its variant states.
Well-written Storybook stories serve three purposes: development (see the component in isolation), documentation (stakeholders browse the component library), and testing (play functions test interactions without a browser test runner). AI-generated stories serve none of these well because they're static snapshots, not interactive, testable documentation.
These rules target Storybook 8+ with CSF3 (Component Story Format 3). CSF3 is a significant API change from CSF2 — the AI needs to know which format to generate.
Rule 1: CSF3 Format with Args
The rule: 'Use CSF3 format for all stories. Export a meta object as default: const meta: Meta<typeof Button> = { component: Button, tags: ["autodocs"] }. Define stories as objects with args: export const Primary: Story = { args: { variant: "primary", children: "Click me" } }. Never use CSF2 Template.bind({}) pattern. Never render components directly in stories — use args for all props.'
For the meta object: 'The meta defines the component, title (optional — auto-inferred from file path), tags for autodocs, argTypes for control customization, decorators for context wrapping, and parameters for Storybook configuration. Keep meta minimal — let Storybook infer what it can.'
AI generates CSF2 (Template.bind) or even the old storiesOf() API from training data. CSF3 is simpler: a default export (meta) + named exports (stories as objects with args). No Template, no bind, no function calls — just objects.
- Default export: Meta<typeof Component> with component + tags
- Named exports: Story objects with args — not Template.bind({})
- args for all props — never hardcoded JSX in the render
- tags: ['autodocs'] for automatic documentation generation
- argTypes for control customization — hide, rename, constrain values
AI generates CSF2 (Template.bind({})) from old training data. CSF3 is simpler: export const Primary: Story = { args: { variant: 'primary' } }. Just objects with args — no Template, no bind, no function calls.
Rule 2: One Story Per Variant State
The rule: 'Create one story per meaningful visual state: export const Primary: Story = { args: { variant: "primary" } }; export const Secondary: Story = { args: { variant: "secondary" } }; export const Disabled: Story = { args: { disabled: true } }; export const Loading: Story = { args: { loading: true } }. Each story represents a state the component can be in — not a random prop combination.'
For naming: 'Name stories as states, not implementations: Primary, Secondary, Disabled, Loading, Error, Empty, WithIcon, LongText — not Test1, Default, MyButton. Story names become the documentation labels in the sidebar. They should be immediately understandable to a non-technical stakeholder.'
For composition: 'For complex components, show composition stories: export const FormWithValidation: Story = { args: { showErrors: true, fields: [...] } }. These demonstrate how the component looks in real usage — not just isolated props. Pair with simple variant stories for the full picture.'
Rule 3: Play Functions for Interaction Testing
The rule: 'Use play functions for stories that involve user interaction: export const Submitted: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.type(canvas.getByLabelText("Email"), "test@example.com"); await userEvent.click(canvas.getByRole("button", { name: "Submit" })); await expect(canvas.getByText("Success")).toBeInTheDocument(); } }. Play functions are interaction tests that run in the Storybook UI and in CI with test-runner.'
For testing library integration: 'Play functions use @storybook/test which re-exports testing-library and vitest's expect. Use within(canvasElement) to scope queries to the story. Use userEvent for realistic user interactions (typing, clicking, tabbing). Use expect for assertions. These tests are portable — they run in the browser (Storybook UI) and headless (test-runner in CI).'
AI generates stories without play functions — they show the component but don't test behavior. Play functions turn stories into interaction tests: type in a field, click a button, verify the result. One story both documents and tests the interaction.
Play functions turn stories into interaction tests: type, click, verify. They run in the Storybook UI (visual) and in CI with test-runner (headless). One story both documents and tests the component.
Rule 4: Decorators for Context and Layout
The rule: 'Use decorators to wrap stories in required context: theme providers, router context, state providers, layout containers. Define in meta for all stories in a file: decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>]. Define in .storybook/preview.ts for global decorators. Never duplicate context setup in every story — use decorators once.'
For common decorators: 'Layout decorator: (Story) => <div style={{ padding: "1rem" }}><Story /></div>. Theme decorator: (Story) => <ThemeProvider theme={theme}><Story /></ThemeProvider>. Router decorator: (Story) => <MemoryRouter><Story /></MemoryRouter>. State decorator: (Story) => <Provider store={mockStore}><Story /></Provider>.'
For parameters: 'Use parameters for Storybook addon configuration: parameters: { layout: "centered" } for centering, parameters: { backgrounds: { default: "dark" } } for background, parameters: { viewport: { defaultViewport: "mobile1" } } for mobile preview. Parameters configure the Storybook UI — decorators configure the component context.'
- Decorators in meta for file-level context — preview.ts for global
- ThemeProvider, Router, StateProvider — wrap once, applies to all stories
- parameters for Storybook UI config: layout, backgrounds, viewport
- Never duplicate context in every story — decorator once in meta
- Decorators compose: global + file-level + story-level, in order
Rule 5: Autodocs and Design System Documentation
The rule: 'Add tags: ["autodocs"] to meta for automatic documentation pages. Autodocs generates a page with: component description (from JSDoc on the component), prop table (from TypeScript types), and all stories rendered with their args. Write JSDoc on the component for the description: /** A button component with variants for primary, secondary, and destructive actions. */.'
For custom docs: 'Use MDX for custom documentation pages that combine prose with live stories: import { Meta, Story, Canvas, Controls } from "@storybook/blocks". Create .mdx files alongside .stories.tsx for design guidelines, usage examples, and do/don't comparisons. MDX pages appear in the sidebar alongside component stories.'
For the design system: 'Create a Foundations section with: Colors (show all design tokens), Typography (show all text styles), Spacing (show the spacing scale), Icons (show all icons). These aren't component stories — they're documentation pages built with MDX that reference your design tokens. They make the design system browsable by designers and developers.'
A JSDoc comment on your component becomes the description on the autodocs page. TypeScript types become the prop table. Stories become live examples. One tag (tags: ['autodocs']) generates the entire documentation page.
Complete Storybook Rules Template
Consolidated rules for Storybook 8+ projects.
- CSF3: Meta<typeof Component> default export + Story objects with args — never CSF2 Template.bind
- One story per visual state: Primary, Secondary, Disabled, Loading, Error, Empty
- Play functions for interaction testing — userEvent + expect — runs in UI and CI
- Decorators for context: theme, router, state — in meta (file) or preview (global)
- tags: ['autodocs'] for automatic doc pages — JSDoc on components for descriptions
- MDX for custom docs: usage guidelines, do/don't, design foundations
- Parameters for Storybook UI: layout, backgrounds, viewport
- test-runner in CI — chromatic for visual regression — a11y addon for accessibility