Storybook - Args and Controls
Configure interactive controls and args to make stories dynamic and explorable, allowing designers and developers to test component variations in real-time.
Key Concepts Args
Args are inputs to components that Storybook tracks and makes interactive:
export const Primary: Story = { args: { label: 'Button', primary: true, size: 'medium', onClick: () => alert('clicked'), }, };
ArgTypes
ArgTypes define metadata about args, including control types and documentation:
const meta = {
component: Button,
argTypes: {
backgroundColor: {
control: 'color',
description: 'Background color of the button',
},
size: {
control: { type: 'select' },
options: ['small', 'medium', 'large'],
description: 'Size variant',
},
onClick: {
action: 'clicked',
},
},
} satisfies Meta
Control Types
Storybook provides various control types for different data types:
text - String input number - Number input with validation boolean - Checkbox toggle color - Color picker date - Date picker select - Dropdown menu radio - Radio buttons range - Slider with min/max object - JSON editor array - Array editor Best Practices 1. Infer Controls from TypeScript
Let Storybook auto-generate controls from TypeScript types:
interface ButtonProps { label: string; primary?: boolean; size?: 'small' | 'medium' | 'large'; backgroundColor?: string; onClick?: () => void; }
export const Button: React.FC
const meta = {
component: Button,
// Controls inferred from ButtonProps
} satisfies Meta
- Customize Control Types When Needed
Override auto-inferred controls for better UX:
const meta = {
component: ColorPicker,
argTypes: {
color: {
control: 'color', // Override default text input
},
opacity: {
control: { type: 'range', min: 0, max: 1, step: 0.1 },
},
preset: {
control: 'select',
options: ['primary', 'secondary', 'success', 'warning', 'danger'],
},
},
} satisfies Meta
- Use Actions for Event Handlers
Track event callbacks in the Actions panel:
const meta = {
component: Form,
argTypes: {
onSubmit: { action: 'submitted' },
onChange: { action: 'changed' },
onError: { action: 'error occurred' },
},
} satisfies Meta
export const Default: Story = { args: { onSubmit: (data) => console.log('Form data:', data), }, };
- Set Sensible Defaults
Provide default args at the meta level:
const meta = {
component: Slider,
args: {
min: 0,
max: 100,
step: 1,
value: 50,
},
argTypes: {
value: {
control: { type: 'range', min: 0, max: 100, step: 1 },
},
},
} satisfies Meta
- Document Args
Add descriptions to help users understand each arg:
const meta = {
component: Tooltip,
argTypes: {
placement: {
control: 'select',
options: ['top', 'right', 'bottom', 'left'],
description: 'Position of the tooltip relative to its trigger',
table: {
defaultValue: { summary: 'top' },
type: { summary: 'string' },
},
},
delay: {
control: { type: 'number', min: 0, max: 2000, step: 100 },
description: 'Delay in milliseconds before showing the tooltip',
},
},
} satisfies Meta
Common Patterns Enum/Union Type Controls type ButtonVariant = 'primary' | 'secondary' | 'danger';
const meta = {
component: Button,
argTypes: {
variant: {
control: 'radio',
options: ['primary', 'secondary', 'danger'] satisfies ButtonVariant[],
},
},
} satisfies Meta
Complex Object Controls
const meta = {
component: Chart,
argTypes: {
data: {
control: 'object',
description: 'Chart data points',
},
options: {
control: 'object',
description: 'Chart configuration',
},
},
} satisfies Meta
export const Default: Story = { args: { data: [ { x: 0, y: 10 }, { x: 1, y: 20 }, { x: 2, y: 15 }, ], options: { showLegend: true, animate: true, }, }, };
Conditional Controls
Hide irrelevant controls based on other arg values:
const meta = {
component: Input,
argTypes: {
type: {
control: 'select',
options: ['text', 'number', 'email', 'password'],
},
min: {
control: 'number',
if: { arg: 'type', eq: 'number' }, // Only show for number inputs
},
max: {
control: 'number',
if: { arg: 'type', eq: 'number' },
},
showPasswordToggle: {
control: 'boolean',
if: { arg: 'type', eq: 'password' },
},
},
} satisfies Meta
Disable Controls
Disable controls for props that shouldn't be editable:
const meta = {
component: DataTable,
argTypes: {
data: {
control: false, // Disable control (use args instead)
},
onSort: {
table: { disable: true }, // Hide from docs table
},
},
} satisfies Meta
Grouping Controls
Organize controls into logical categories:
const meta = { component: Modal, argTypes: { // Appearance title: { control: 'text', table: { category: 'Appearance' }, }, size: { control: 'select', options: ['small', 'medium', 'large'], table: { category: 'Appearance' }, },
// Behavior
closeOnEscape: {
control: 'boolean',
table: { category: 'Behavior' },
},
closeOnOverlayClick: {
control: 'boolean',
table: { category: 'Behavior' },
},
// Events
onClose: {
action: 'closed',
table: { category: 'Events' },
},
},
} satisfies Meta
Advanced Patterns Custom Control Components
Create custom controls for specialized inputs:
import { useArgs } from '@storybook/preview-api';
const meta = {
component: GradientPicker,
argTypes: {
gradient: {
control: {
type: 'object',
},
},
},
} satisfies Meta
export const Custom: Story = {
render: (args) => {
const [{ gradient }, updateArgs] = useArgs();
return (
Dynamic ArgTypes
Generate argTypes programmatically:
const themes = ['light', 'dark', 'system'] as const;
const meta = {
component: ThemeProvider,
argTypes: {
theme: {
control: 'select',
options: themes,
mapping: Object.fromEntries(
themes.map(theme => [theme, theme])
),
},
},
} satisfies Meta
Anti-Patterns ❌ Don't Override Args in Render // Bad export const Example: Story = { render: (args) => , };
// Good export const Example: Story = { args: { label: 'Hardcoded', }, };
❌ Don't Use Controls for Static Data // Bad - Large mock data in controls export const WithData: Story = { args: { items: Array.from({ length: 1000 }, (_, i) => ({ id: i, ... })), }, argTypes: { items: { control: 'object' }, // Don't make editable }, };
// Good - Disable control for mock data export const WithData: Story = { args: { items: mockLargeDataset, }, argTypes: { items: { control: false }, }, };
❌ Don't Duplicate ArgTypes Across Stories // Bad export const Story1: Story = { argTypes: { size: { control: 'select', options: ['small', 'large'] }, }, }; export const Story2: Story = { argTypes: { size: { control: 'select', options: ['small', 'large'] }, }, };
// Good - Define at meta level
const meta = {
component: Button,
argTypes: {
size: { control: 'select', options: ['small', 'large'] },
},
} satisfies Meta
Related Skills storybook-story-writing: Writing well-structured stories storybook-component-documentation: Auto-generating docs from controls storybook-play-functions: Testing interactions with args