--- name: storybook-story-writing user-invocable: false description: Use when creating or modifying Storybook stories for components. Ensures stories follow CSF3 format, properly showcase component variations, and build successfully. allowed-tools: - Read - Write - Edit - Bash - Grep - Glob --- # Storybook - Story Writing Write well-structured, maintainable Storybook stories using Component Story Format 3 (CSF3) that showcase component variations and ensure consistent rendering. ## Key Concepts ### Component Story Format 3 (CSF3) CSF3 is the modern Storybook format that uses object syntax for stories: ```typescript import type { Meta, StoryObj } from '@storybook/react'; import { Button } from './Button'; const meta = { title: 'Components/Button', component: Button, parameters: { layout: 'centered', }, tags: ['autodocs'], argTypes: { backgroundColor: { control: 'color' }, }, } satisfies Meta; export default meta; type Story = StoryObj; export const Primary: Story = { args: { primary: true, label: 'Button', }, }; export const Secondary: Story = { args: { label: 'Button', }, }; ``` ### Story Organization - One story file per component: `Component.stories.tsx` - Use descriptive story names: `Primary`, `Secondary`, `Large`, `Disabled` - Group related stories under a title hierarchy: `Components/Forms/Input` ### Default Export (Meta) The default export defines metadata for all stories: ```typescript const meta = { title: 'Components/Button', // Navigation path component: Button, // Component reference parameters: {}, // Story-level config tags: ['autodocs'], // Enable auto-documentation argTypes: {}, // Control types decorators: [], // Wrappers for stories } satisfies Meta; ``` ## Best Practices ### 1. Use TypeScript for Type Safety ```typescript import type { Meta, StoryObj } from '@storybook/react'; const meta = { component: Button, } satisfies Meta; type Story = StoryObj; ``` ### 2. Show All Component States Create stories for each meaningful state: ```typescript export const Default: Story = { args: { label: 'Click me', }, }; export const Loading: Story = { args: { label: 'Loading...', loading: true, }, }; export const Disabled: Story = { args: { label: 'Disabled', disabled: true, }, }; export const WithIcon: Story = { args: { label: 'Download', icon: 'download', }, }; ``` ### 3. Use Sensible Defaults ```typescript export const Primary: Story = { args: { primary: true, label: 'Button', size: 'medium', }, }; // Extend existing stories export const PrimaryLarge: Story = { ...Primary, args: { ...Primary.args, size: 'large', }, }; ``` ### 4. Add Descriptive Parameters ```typescript export const WithTooltip: Story = { args: { label: 'Hover me', tooltip: 'Click to submit', }, parameters: { docs: { description: { story: 'Shows a tooltip on hover to provide additional context.', }, }, }, }; ``` ### 5. Use Decorators for Context ```typescript import { RouterDecorator } from '../decorators'; const meta = { component: Navigation, decorators: [ (Story) => (
), RouterDecorator, ], } satisfies Meta; ``` ## Common Patterns ### Form Components ```typescript export const EmptyForm: Story = { args: { onSubmit: (data) => console.log(data), }, }; export const PrefilledForm: Story = { args: { defaultValues: { email: 'user@example.com', name: 'John Doe', }, }, }; export const WithValidationErrors: Story = { args: { errors: { email: 'Invalid email format', name: 'Name is required', }, }, }; ``` ### Layout Components ```typescript export const WithSidebar: Story = { args: { sidebar: , children: , }, parameters: { layout: 'fullscreen', }, }; ``` ### Data-Driven Components ```typescript const mockData = [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }, { id: 3, name: 'Item 3' }, ]; export const WithData: Story = { args: { items: mockData, }, }; export const Empty: Story = { args: { items: [], emptyMessage: 'No items found', }, }; ``` ### Responsive Components ```typescript export const Mobile: Story = { args: { variant: 'mobile', }, parameters: { viewport: { defaultViewport: 'mobile1', }, }, }; export const Desktop: Story = { args: { variant: 'desktop', }, parameters: { viewport: { defaultViewport: 'desktop', }, }, }; ``` ## Anti-Patterns ### ❌ Don't Use Template Binding (CSF2) ```typescript // Bad - Old CSF2 format const Template = (args) =>