--- name: storybook-args-controls user-invocable: false description: Use when configuring interactive controls and args for Storybook stories. Helps create dynamic, explorable component demonstrations with proper type constraints. allowed-tools: - Read - Write - Edit - Bash - Grep - Glob --- # 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: ```typescript 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: ```typescript 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: ```typescript 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; ``` ### 2. Customize Control Types When Needed Override auto-inferred controls for better UX: ```typescript 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; ``` ### 3. Use Actions for Event Handlers Track event callbacks in the Actions panel: ```typescript 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), }, }; ``` ### 4. Set Sensible Defaults Provide default args at the meta level: ```typescript 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; ``` ### 5. Document Args Add descriptions to help users understand each arg: ```typescript 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 ```typescript type ButtonVariant = 'primary' | 'secondary' | 'danger'; const meta = { component: Button, argTypes: { variant: { control: 'radio', options: ['primary', 'secondary', 'danger'] satisfies ButtonVariant[], }, }, } satisfies Meta; ``` ### Complex Object Controls ```typescript 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: ```typescript 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: ```typescript 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: ```typescript 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: ```typescript 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 ( updateArgs({ gradient: newGradient })} /> ); }, }; ``` ### Dynamic ArgTypes Generate argTypes programmatically: ```typescript 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 ```typescript // Bad export const Example: Story = { render: (args) =>