Skip to content

Addons & Tooling

FieldValue
TypeAgent Reference
Source~/.copilot/agents/_refs/storybook-designer/addons-and-tooling.md
DescriptionNot specified

Source Content

Addons & Tooling

Addon Stack

AddonPackagePurpose
A11y@storybook/addon-a11yaxe-powered accessibility panel; runs in CI
Interactions@storybook/testplay functions, userEvent, expect, within
Controlsbuilt-inAuto-generated from TypeScript props
Viewportbuilt-inMobile/tablet/desktop preview switching
Backgroundsbuilt-inLight, dark, custom tonal backgrounds
Docsbuilt-inAuto-generated docs from CSF3 + createComponentDocs
MSWmsw-storybook-addonAPI mocking in stories (requires msw setup)

Accessibility Addon

Every story should pass the a11y addon panel before shipping. The panel runs axe-core automatically.

Configuring a11y per story:

export const Default: Story = {
parameters: {
a11y: {
// Disable a specific rule only when there is a documented reason
config: {
rules: [
{
id: 'color-contrast',
// Only disable in a story that intentionally shows a disabled state
enabled: false,
},
],
},
},
},
}

Never globally disable a11y rules. Story-level overrides require a comment explaining why.

Interactions Addon Setup

Install: @storybook/test (bundled with Storybook 8, no separate install needed).

The test runner (@storybook/test-runner) runs all play functions in CI headlessly:

Terminal window
# Run interaction tests
pnpm test # runs via the configured test script
# OR directly:
npx storybook test --url http://localhost:6006

Configure in package.json:

{
"scripts": {
"test-storybook": "storybook test --url http://localhost:6006"
}
}

MSW Addon for API Mocking

Use msw-storybook-addon to mock API calls in stories that need server data:

.storybook/preview.tsx
import { initialize, mswLoader } from 'msw-storybook-addon'
initialize()
const preview = {
loaders: [mswLoader],
}
export default preview
// In a story
import { http, HttpResponse } from 'msw'
export const WithData: Story = {
parameters: {
msw: {
handlers: [
http.get('/api/users', () => {
return HttpResponse.json([
{ id: '1', name: 'Sarah Okafor', email: 'sarah@example.com', role: 'admin' },
])
}),
],
},
},
}
export const ErrorState: Story = {
parameters: {
msw: {
handlers: [
http.get('/api/users', () => {
return HttpResponse.error()
}),
],
},
},
}

Preview Decorators

Wrap stories in necessary providers via .storybook/preview.tsx decorators — not in individual story files:

.storybook/preview.tsx
import type { Preview } from '@storybook/react-vite'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ThemeProvider } from '../src/components/theme-provider'
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false, staleTime: 0 } },
})
const preview: Preview = {
decorators: [
(Story) => (
<QueryClientProvider client={queryClient}>
<ThemeProvider defaultTheme="light">
<Story />
</ThemeProvider>
</QueryClientProvider>
),
],
parameters: {
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: 'hsl(44 35% 96%)' }, // --brand-mist light
{ name: 'dark', value: 'hsl(217 22% 18%)' }, // --brand-mist dark
{ name: 'white', value: '#ffffff' },
],
},
viewport: {
viewports: {
mobile: { name: 'Mobile', styles: { width: '390px', height: '844px' } },
tablet: { name: 'Tablet', styles: { width: '768px', height: '1024px' } },
desktop: { name: 'Desktop', styles: { width: '1440px', height: '900px' } },
},
},
},
}
export default preview

Dark Mode in Stories

For components with dark mode support, add a dark mode story using the backgrounds parameter:

export const DarkMode: Story = {
parameters: {
backgrounds: { default: 'dark' },
// Apply the night theme data attribute
theme: 'night',
},
decorators: [
(Story) => (
<div data-theme="night">
<Story />
</div>
),
],
}

Icon Controls

Use the shared icon picker helpers — never write per-story option lists:

import { iconControl, iconArgType } from '@/lib/storybook-icon-controls'
const meta = {
component: Button,
argTypes: {
leadingIcon: iconArgType, // gives a searchable icon picker in Controls
trailingIcon: iconArgType,
},
} satisfies Meta<typeof Button>
export const WithIcon: Story = {
args: {
leadingIcon: iconControl('Plus'),
children: 'Add item',
},
}

createComponentDocs

All fields:

createComponentDocs({
summary: string, // required — one sentence
when: string, // required — when to use this component
whenNot: string, // required — when NOT to use it (name alternatives)
motion?: string, // describe animation when present
reducedMotion?: string, // describe reduced-motion behavior
a11y?: string, // notable accessibility behavior
status?: 'stable' | 'experimental' | 'deprecated',
})

Storybook Configuration Files

.storybook/
main.ts ← addon list, framework, staticDirs, features
preview.tsx ← global decorators, parameters, loaders
manager.ts ← sidebar theme, panel position (rarely needed)

main.ts essentials:

import type { StorybookConfig } from '@storybook/react-vite'
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(ts|tsx|mdx)'],
addons: [
'@storybook/addon-a11y',
'@storybook/addon-interactions',
'@storybook/addon-docs',
'msw-storybook-addon',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
staticDirs: ['../public'],
features: {
// Storybook 8 features
},
}
export default config