Introduction
A design system—comprising reusable components, guidelines, and tokens—ensures consistency, speeds up development, and empowers teams to scale UI collaboratively. When paired with React and Storybook, you get a living, interactive component library that both designers and developers can explore, test, and extend. In this guide, we’ll walk through setting up Storybook in a React project, structuring your component library, defining design tokens, writing accessible, themeable components, and automating documentation and testing. By the end, you’ll have a robust workflow to craft, showcase, and maintain a scalable design system.

1. Why Use Storybook and React for Your Design System
1.1 Benefits of a Component-Driven Workflow
- Isolation: Build components in isolation, without the complexity of your app.
- Collaboration: Designers and stakeholders can review components in Storybook’s UI.
- Documentation: Stories double as living documentation, always in sync with code.
- Testing: Integrate visual and interaction tests (e.g., Chromatic, Jest).
1.2 Why React?
- Declarative Components: JSX makes UI structure clear and testable.
- Ecosystem: Rich tooling for styling (CSS-in-JS, Tailwind), state management, and testing.
- Community: Large library of prebuilt components and best practices.
2. Initial Setup
2.1 Create a React Project
If you don’t already have one:
bashCopyEditnpx create-react-app my-design-system
cd my-design-system
2.2 Install and Initialize Storybook
bashCopyEditnpx sb init
This command:
- Detects React and configures Storybook.
- Installs
@storybook/react
and related dependencies. - Adds a
.storybook
directory with default config.
2.3 Run Storybook
bashCopyEditnpm run storybook
Open http://localhost:6006
to see the default component examples.
3. Organizing Your Design System
3.1 Folder Structure
Adopt a clear directory layout:

cssCopyEditsrc/
components/
Button/
Button.tsx
Button.stories.tsx
Button.test.tsx
Button.styles.ts (or .module.css / styled.ts)
Input/
Card/
tokens/
colors.ts
typography.ts
spacing.ts
themes/
light.ts
dark.ts
3.2 Design Tokens
Centralize your design decisions:
tsCopyEdit// src/tokens/colors.ts
export const colors = {
primary: '#4F46E5',
accent: '#FBBF24',
text: '#333',
background: '#fff',
surface: '#f5f5f5',
};
Use tokens in components to ensure consistent theming.
4. Building Your First Component: Button
4.1 Component Implementation
tsxCopyEdit// src/components/Button/Button.tsx
import React from 'react';
import './Button.styles.css';
type ButtonProps = {
children: React.ReactNode;
variant?: 'primary' | 'secondary';
disabled?: boolean;
onClick?: () => void;
};
export const Button: React.FC<ButtonProps> = ({
children,
variant = 'primary',
disabled = false,
onClick,
}) => (
<button
className={`btn btn--${variant}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
cssCopyEdit/* src/components/Button/Button.styles.css */
.btn {
font-weight: 600;
padding: 0.5rem 1rem;
border-radius: 4px;
border: none;
cursor: pointer;
transition: background-color 0.2s ease;
}
.btn--primary {
background-color: var(--color-primary);
color: white;
}
.btn--primary:hover:not(:disabled) {
background-color: #4338ca;
}
.btn--secondary {
background-color: var(--color-surface);
color: var(--color-text);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
4.2 Writing Stories

tsxCopyEdit// src/components/Button/Button.stories.tsx
import React from 'react';
import { Meta, Story } from '@storybook/react';
import { Button, ButtonProps } from './Button';
export default {
title: 'Components/Button',
component: Button,
argTypes: { onClick: { action: 'clicked' } },
} as Meta;
const Template: Story<ButtonProps> = (args) => <Button {...args} />;
export const Primary = Template.bind({});
Primary.args = { variant: 'primary', children: 'Primary Button' };
export const Secondary = Template.bind({});
Secondary.args = { variant: 'secondary', children: 'Secondary Button' };
export const Disabled = Template.bind({});
Disabled.args = { disabled: true, children: 'Disabled' };
Storybook will render each state, providing interactive controls.
5. Theming and CSS Variables
5.1 Define CSS Variables
Add to your global CSS:
cssCopyEdit:root {
--color-primary: #4F46E5;
--color-accent: #FBBF24;
--color-text: #333333;
--color-background: #ffffff;
--color-surface: #f5f5f5;
}
/* Dark theme override */
[data-theme="dark"] {
--color-background: #121212;
--color-surface: #1e1e1e;
--color-text: #eeeeee;
}
5.2 Toggle Theme in Storybook
Install the theming addon:
bashCopyEditnpm install @storybook/addon-backgrounds --save-dev
Configure in .storybook/main.js
:
jsCopyEditmodule.exports = {
addons: [
'@storybook/addon-essentials',
'@storybook/addon-backgrounds',
{
name: '@storybook/addon-postcss',
options: { postcssLoaderOptions: { implementation: require('postcss') } },
},
],
};
Add toolbar for theme switching in .storybook/preview.js
:
jsCopyEditexport const globalTypes = {
theme: {
name: 'Theme',
defaultValue: 'light',
toolbar: {
icon: 'circlehollow',
items: ['light', 'dark'],
},
},
};
export const decorators = [
(Story, context) => {
document.documentElement.setAttribute('data-theme', context.globals.theme);
return <Story />;
},
];
6. Ensuring Accessibility

6.1 Contrast and Focus States
- Use tools like axe or Lighthouse to audit.
- Provide visible focus rings: cssCopyEdit
.btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
6.2 Storybook Accessibility Addon
bashCopyEditnpm install @storybook/addon-a11y --save-dev
Enable it in .storybook/main.js
:
jsCopyEditaddons: ['@storybook/addon-a11y', /* ... */],
Now you can view accessibility reports per story.
7. Automated Visual Testing
7.1 Integrate Chromatic
Chromatic captures and compares snapshots:
- Install: bashCopyEdit
npm install --save-dev chromatic
- Add Script: in
package.json
: jsonCopyEdit"scripts": { "chromatic": "chromatic --project-token=YOUR_PROJECT_TOKEN" }
- Run: bashCopyEdit
npm run chromatic
Chromatic will surface visual regressions as UI changes.
8. Versioning and Publishing
8.1 Semantic Versioning
- Bump major for breaking changes, minor for new components, patch for fixes.
- Use
standard-version
orsemantic-release
to automate changelog generation.
8.2 Packaging as an NPM Module
Configure package.json
:
jsonCopyEdit{
"name": "@your-org/design-system",
"version": "1.0.0",
"main": "dist/index.js",
"files": ["dist"],
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0"
}
}
Build with your preferred bundler (Rollup, Vite), then:
bashCopyEditnpm publish --access public
Consumers install via:
bashCopyEditnpm install @your-org/design-system
9. Maintaining and Evolving Your System

9.1 Component Guidelines
Document usage, best practices, and do’s/don’ts directly in Storybook or a companion site using Storybook Docs.
9.2 Deprecation Strategy
- Mark Deprecated: Tag old stories as deprecated.
- Alternatives: Suggest replacement components in notes.
- Removal Cycle: After two minor versions, remove deprecated code.
9.3 Community Feedback
- Encourage pull requests for new components or token updates.
- Use GitHub Issues or Discussions for proposals and RFCs.
Conclusion
By combining React’s composable components with Storybook’s interactive environment, you establish a living design system that scales from prototypes to production. Centralize your design tokens, enforce theming with CSS variables, ensure accessibility compliance, and automate visual testing to maintain quality. Version and publish your library for easy consumption, and foster an open feedback loop with your team. With this workflow, building and evolving consistent, documented UI at scale becomes an integral—and enjoyable—part of your development process.