CSF — Component Story Format

Modern Storybook describes stories as ordinary ES modules: meta + named exports. That is easy to type, review in PRs, and generate docs from.

Button.stories.jsx skeleton

import { Button } from './Button';

export default {
  title: 'Components/Button',
  component: Button,
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'outline', 'ghost', 'destructive'],
    },
    size: { control: 'inline-radio', options: ['sm', 'md', 'lg'] },
    disabled: { control: 'boolean' },
    onClick: { action: 'clicked' },
  },
  args: {
    children: 'Save',
    variant: 'primary',
    size: 'md',
  },
};

export const Primary = {};

export const Secondary = {
  args: { variant: 'secondary' },
};

export const Loading = {
  args: { loading: true, children: 'Saving…' },
};

export const WithClick = {
  args: { onClick: () => {} },
};

Primary = {} inherits args from meta — less duplication.

Controls

Controls let you tweak props without editing code. Configure argTypes:

  • select / radio for enum variants;
  • boolean for flags;
  • text for children (careful with ReactNode);
  • disable what is unsafe to tweak (control: false for a complex icon).

Actions

The Actions addon logs callbacks (onClick, onOpenChange). In argTypes set action: 'clicked', or use fn() from storybook/test in SB8 — so the Actions panel shows interaction.

Decorators

Wrappers for context:

export default {
  component: Modal,
  decorators: [
    (Story) => (
      <div style={{ minHeight: 320 }}>
        <Story />
      </div>
    ),
  ],
};

For themes — a global decorator in preview with data-theme on documentElement.

State matrices

For a DS, “gallery” stories are useful:

export const AllVariants = {
  render: () => (
    <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
      <Button variant="primary">Primary</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="ghost">Ghost</Button>
    </div>
  ),
  parameters: { controls: { disable: true } },
};

Those pages catch visual regressions across a whole variant family at once.

Naming

  • title: Components/Button, Forms/Input, Feedback/Badge.
  • Export names: Primary, Disabled, WithError — no “Test…” verbs.
  • One story — one clear scenario; do not mix loading and destructive without a reason.

Practice

Write CSF for Button and Input: default args, 4–6 states, actions on events, a variants matrix. Open Controls and confirm the enums match the component API.