DEV Community

Cover image for Building and Documenting UI Components with Storybook and React
Debajit Mallick
Debajit Mallick

Posted on

1

Building and Documenting UI Components with Storybook and React

Introduction

Have you ever struggled with maintaining consistency across your UI components or found it challenging to collaborate with designers? I've been there too. After experimenting with several tools, I discovered that Storybook changed my development workflow for the better.

In this post, I'll walk you through setting up Storybook in a React project and show you how to build and document components that you can reuse throughout your applications.

What is Storybook?

It lets you build and test UI components in isolation, away from the complexity of the full app. The interactive documentation makes it easier for team members to understand how components work. Using Storybook, Setting up visual regression tests became surprisingly simple

Getting Started: Project Setup

Let's start by creating our project foundation. Fire up your terminal and run:

npm create vite@latest my-storybook-app -- --template react-ts
cd my-storybook-app
npm install
Enter fullscreen mode Exit fullscreen mode

With our React project ready, let's add Storybook to the mix:

npx storybook@latest init
Enter fullscreen mode Exit fullscreen mode

This command works like magic – it automatically configures Storybook for your Vite setup, creates a .storybook/ folder, and adds some example stories in src/stories/. Take a moment to explore these files to get familiar with the structure.

Creating Our First Component: The Button

Let's start by building a flexible Button component that we can reuse across our application.

First, create the component file:

// src/components/Button.tsx
import React from 'react';

export interface ButtonProps {
  label: string;
  onClick?: () => void;
  variant?: 'primary' | 'secondary';
}

export const Button: React.FC<ButtonProps> = ({
  label,
  onClick,
  variant = 'primary',
}) => {
  const styles = {
    primary: 'bg-blue-500 text-white',
    secondary: 'bg-gray-200 text-black',
  };

  return (
    <button
      className={`px-4 py-2 rounded ${styles[variant]}`}
      onClick={onClick}
    >
      {label}
    </button>
  );
};
Enter fullscreen mode Exit fullscreen mode

Now for the fun part – creating stories for our Button:

// src/components/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = {
  args: {
    label: 'Click Me',
    variant: 'primary',
  },
};

export const Secondary: Story = {
  args: {
    label: 'Cancel',
    variant: 'secondary',
  },
};
Enter fullscreen mode Exit fullscreen mode

Building a Card Component

Now that we have our Button, let's create a Card component that can even incorporate our Button.

// src/components/Card.tsx
import React from 'react';

export interface CardProps {
  title: string;
  description: string;
  children?: React.ReactNode;
}

export const Card: React.FC<CardProps> = ({ title, description, children }) => {
  return (
    <div className="border rounded-lg shadow-md p-4 max-w-sm">
      <h3 className="text-lg font-bold mb-2">{title}</h3>
      <p className="text-gray-700 mb-4">{description}</p>
      {children}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

And here's how we can showcase it in Storybook:

// src/components/Card.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Card } from './Card';
import { Button } from './Button';

const meta: Meta<typeof Card> = {
  title: 'Components/Card',
  component: Card,
};

export default meta;
type Story = StoryObj<typeof Card>;

export const Default: Story = {
  args: {
    title: 'Welcome!',
    description: 'This is a sample card component.',
  },
};

export const WithButton: Story = {
  render: (args) => (
    <Card {...args}>
      <Button label="Learn More" />
    </Card>
  ),
  args: {
    title: 'Interactive Card',
    description: 'This card includes a button as a child component.',
  },
};
Enter fullscreen mode Exit fullscreen mode

I particularly love this example because it shows how components can be nested in Storybook stories, helping you visualize complex component relationships.

Adding Some Style with Tailwind (Optional)

If you're like me and prefer Tailwind for styling, it's super easy to add:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

Update your CSS with:

/* in index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Don't forget to add your source folder to the content paths in tailwind.config.js!

Time to See It in Action!

Let's fire up Storybook and see our components:

npm run storybook
Enter fullscreen mode Exit fullscreen mode

When you visit http://localhost:6006/, you'll see your Button and Card components displayed in the Storybook UI. Try clicking around and interacting with different props – it's incredibly satisfying to see your components respond in real-time.

Sharing Your Component Library

Want to share your beautiful components with the team or showcase them in a portfolio? Generate a static build:

npm run build-storybook
Enter fullscreen mode Exit fullscreen mode

This creates a storybook-static/ directory that you can deploy to Netlify, Vercel, GitHub Pages, or any static hosting provider. I've found this especially useful when working with remote teams.

Conclusion

Using Storybook you can develop a consistent design system. Which helps you to onboard new team members more quickly, catch UI bugs before they reach production. Also, you can Communicate more effectively with designers. If you like this blog and want to learn more about Frontend Development and Software Engineering, you can follow me on Dev.to.

Top comments (0)