Building a Design System with Storybook and React

Table of Contents
Big thanks to our contributors those make our blogs possible.

Our growing community of contributors bring their unique insights from around the world to power our blog. 

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:

  1. Detects React and configures Storybook.
  2. Installs @storybook/react and related dependencies.
  3. 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:

  1. Install: bashCopyEditnpm install --save-dev chromatic
  2. Add Script: in package.json: jsonCopyEdit"scripts": { "chromatic": "chromatic --project-token=YOUR_PROJECT_TOKEN" }
  3. Run: bashCopyEditnpm 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 or semantic-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.

Let's connect on TikTok

Join our newsletter to stay updated

Sydney Based Software Solutions Professional who is crafting exceptional systems and applications to solve a diverse range of problems for the past 10 years.

Share the Post

Related Posts