Use when creating or modifying Storybook stories for components. Ensures stories follow CSF3 format, properly showcase component variations, and build successfully.
Limited to specific tools
Additional assets for this skill
This skill is limited to using the following tools:
name: storybook-story-writing description: Use when creating or modifying Storybook stories for components. Ensures stories follow CSF3 format, properly showcase component variations, and build successfully. allowed-tools:
Write well-structured, maintainable Storybook stories using Component Story Format 3 (CSF3) that showcase component variations and ensure consistent rendering.
CSF3 is the modern Storybook format that uses object syntax for stories:
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<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary: Story = {
args: {
primary: true,
label: 'Button',
},
};
export const Secondary: Story = {
args: {
label: 'Button',
},
};
Component.stories.tsxPrimary, Secondary, Large, DisabledComponents/Forms/InputThe default export defines metadata for all stories:
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<typeof Button>;
import type { Meta, StoryObj } from '@storybook/react';
const meta = {
component: Button,
} satisfies Meta<typeof Button>;
type Story = StoryObj<typeof meta>;
Create stories for each meaningful state:
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',
},
};
export const Primary: Story = {
args: {
primary: true,
label: 'Button',
size: 'medium',
},
};
// Extend existing stories
export const PrimaryLarge: Story = {
...Primary,
args: {
...Primary.args,
size: 'large',
},
};
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.',
},
},
},
};
import { RouterDecorator } from '../decorators';
const meta = {
component: Navigation,
decorators: [
(Story) => (
<div style={{ padding: '3rem' }}>
<Story />
</div>
),
RouterDecorator,
],
} satisfies Meta<typeof Navigation>;
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',
},
},
};
export const WithSidebar: Story = {
args: {
sidebar: <Sidebar items={sidebarItems} />,
children: <Content />,
},
parameters: {
layout: 'fullscreen',
},
};
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',
},
};
export const Mobile: Story = {
args: {
variant: 'mobile',
},
parameters: {
viewport: {
defaultViewport: 'mobile1',
},
},
};
export const Desktop: Story = {
args: {
variant: 'desktop',
},
parameters: {
viewport: {
defaultViewport: 'desktop',
},
},
};
// Bad - Old CSF2 format
const Template = (args) => <Button {...args} />;
export const Primary = Template.bind({});
Primary.args = { label: 'Button' };
// Good - CSF3 format
export const Primary: Story = {
args: { label: 'Button' },
};
// Bad
export const Complex: Story = {
render: (args) => {
const [state, setState] = useState(false);
useEffect(() => {
// Complex side effects
}, []);
return <Component {...args} />;
},
};
// Good - Move logic to component or use play functions
export const Complex: Story = {
args: { initialState: false },
};
// Bad
export const Story1: Story = {
args: { label: 'Button', size: 'medium', theme: 'light' },
};
export const Story2: Story = {
args: { label: 'Submit', size: 'medium', theme: 'light' },
};
// Good - Use meta-level defaults
const meta = {
component: Button,
args: {
size: 'medium',
theme: 'light',
},
} satisfies Meta<typeof Button>;
export const Story1: Story = {
args: { label: 'Button' },
};
export const Story2: Story = {
args: { label: 'Submit' },
};
// Bad - Missing type annotation
export const Primary = {
args: { label: 'Button' },
};
// Good - With type
export const Primary: Story = {
args: { label: 'Button' },
};