Storybook Development
Story Structure (CSF 3.0)
import type { Meta, StoryObj } from '@storybook/react'; import { Button } from './Button';
const meta: Meta<typeof Button> = { title: 'Atoms/Button', component: Button, parameters: { layout: 'centered', }, tags: ['autodocs'], argTypes: { variant: { control: 'select', options: ['primary', 'secondary', 'ghost'], description: 'Visual style variant', }, size: { control: 'radio', options: ['sm', 'md', 'lg'], }, disabled: { control: 'boolean' }, onClick: { action: 'clicked' }, }, args: { children: 'Button', variant: 'primary', size: 'md', }, };
export default meta; type Story = StoryObj<typeof meta>;
export const Primary: Story = { args: { variant: 'primary', }, };
export const Secondary: Story = { args: { variant: 'secondary', }, };
export const AllVariants: Story = { render: (args) => ( <div style={{ display: 'flex', gap: '1rem' }}> <Button {...args} variant="primary">Primary</Button> <Button {...args} variant="secondary">Secondary</Button> <Button {...args} variant="ghost">Ghost</Button> </div> ), };
File Naming
ComponentName/ ├── ComponentName.tsx ├── ComponentName.stories.tsx # Stories file ├── ComponentName.mdx # Optional: Extended docs
Controls Configuration
Control Type Use For
select
Enum with many options
radio
Enum with 2-4 options
boolean
True/false toggles
text
String inputs
number
Numeric values
color
Color pickers
object
Complex objects
date
Date values
Common Patterns
Interactive Story with State
export const WithState: Story = { render: function Render(args) { const [count, setCount] = useState(0); return ( <Button {...args} onClick={() => setCount(c => c + 1)}> Clicked {count} times </Button> ); }, };
Story with Decorators
const meta: Meta<typeof Card> = { component: Card, decorators: [ (Story) => ( <div style={{ padding: '2rem', background: '#f5f5f5' }}> <Story /> </div> ), ], };
Responsive Preview
export const Responsive: Story = { parameters: { viewport: { viewports: { mobile: { name: 'Mobile', styles: { width: '375px', height: '667px' } }, tablet: { name: 'Tablet', styles: { width: '768px', height: '1024px' } }, }, defaultViewport: 'mobile', }, }, };
Organization by Atomic Design
Stories are grouped by title in the Storybook sidebar following Atomic Design:
// Atoms title: 'Atoms/Button' title: 'Atoms/Input' title: 'Atoms/Icon'
// Molecules
title: 'Molecules/SearchField'
title: 'Molecules/FormField'
// Organisms title: 'Organisms/Header' title: 'Organisms/ProductCard'
// Templates title: 'Templates/DashboardLayout'
// Pages title: 'Pages/HomePage'
Story files are located directly with the components:
src/components/ ├── atoms/ │ ├── Button/ │ │ ├── Button.tsx │ │ ├── Button.module.css │ │ ├── Button.stories.tsx # title: 'Atoms/Button' │ │ └── index.ts │ └── Input/ │ └── Input.stories.tsx # title: 'Atoms/Input' ├── molecules/ │ └── SearchField/ │ └── SearchField.stories.tsx # title: 'Molecules/SearchField' └── organisms/ └── Header/ └── Header.stories.tsx # title: 'Organisms/Header'
Best Practices
-
One story file per component
-
Use tags: ['autodocs'] for auto-generated docs
-
Provide meaningful default args
-
Show all variants in an overview story
-
Use actions for event callbacks
-
Document edge cases (loading, error, empty states)