DEV Community

Cover image for Beginner Guide to React Storybook
smrpdl1991
smrpdl1991

Posted on

Beginner Guide to React Storybook

## Storybook

Storybook is a powerful open source tool that allows developers to develop UI components isolated from business logic and context of app.

Storybook provides an interactive playground to develop and browse your component.

Change on one component won't affect the others.

Without embedding it to your app, it can serve as a workshop environment to see how components look and behave in various state.

## Getting Started with Storybook

Developing a reusable UI is important aspect for developing modern web app, which will enable us to efficient and streamlined development process.

In this article, we will explore how to create a robust UI components using React, Storybook, Typescript and tailwind.

## Setting up the environment

  1. Install Node.js and npm (Node Package Manager) on your pc. You can
    download it from (https://nodejs.org/en)

  2. Create a New Folder with name learn_storybook for your ui library and
    navigate to its terminal . Run following command:

npm create vite@latest my-storybook-project -- --template react-ts
Enter fullscreen mode Exit fullscreen mode

This command will create a new react app with typescript template . Your project name here is my-storybook-project. You can change it if you want.

cd my-storybook-project
Enter fullscreen mode Exit fullscreen mode

Installing dependencies

npx storybook@latest init
Enter fullscreen mode Exit fullscreen mode

Image description

This will setup Storybook.

npm install -D tailwindcss postcss autoprefixer
Enter fullscreen mode Exit fullscreen mode

This will install Tailwind css , postcss and autoprefixer.

## Configuring Tailwind css

  1. Create tailwind.config.ts
export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: { 
    },
  },
  plugins: [],
};

Enter fullscreen mode Exit fullscreen mode
  1. create a postcss.config.js file in the project root
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}
Enter fullscreen mode Exit fullscreen mode
  1. Finally, import Tailwind css in your src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode
  1. In Storybook configuration folder i.e. .storybook, open preview.ts and add
import "../src/index.css";
Enter fullscreen mode Exit fullscreen mode

Now , our environment is setup , we will now create a components inside src folder .

Lets create a folder called components (you can delete the stories folder, inside src which was created by default while we setup the storybook ,if you want).

You can create a new folder inside the components directory for each ui component which you want to work with.

Here , let's create a folder called breadcrumb inside components folder. We will now create a breadcrumb component and it's stories.

Inside breadcrumb folder , create Breadcrumb.tsx file and docs folder. Inside docs folder , create Breadcrumb.stories.tsx file as in the figure :

Image description

Now add this code inside BreadCrumb.tsx

import React, { ReactNode } from "react";

/* eslint-disable @typescript-eslint/no-explicit-any */
export function slugify(text: any) {
  return text
    .toString()
    .toLowerCase()
    .replace(/\s+/g, "-") // Replace spaces with -
    .replace(/[^\w-]+/g, "") // Remove all non-word characters
    .replace(/--+/g, "-") // Replace multiple - with single -
    .replace(/^-+/, "") // Trim - from start of text
    .replace(/-+$/, ""); // Trim - from end of text
}

interface BreadcrumbsProps {
  title: string | ReactNode;
  path?: { id: number; link: string; label: string }[];
  children?: ReactNode;
  previcon?: React.JSX.Element;
  onClick?: () => void;
}
const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
  title,
  path,
  children,
  previcon,
  onClick,
}) => {
  return (
    <div
      className={`breadcrumb  bg-danger-0 px-6 py-4 flex  items-center gap-5 breadcrumb-${
        title ? slugify(title) : "default"
      }`}
    >
      {previcon && (
        <div
          className="icon w-[36px] h-[36px] inline-flex items-center justify-center border border-neutral-200 rounded cursor-pointer"
          onClick={onClick}
        >
          {previcon}
        </div>
      )}
      <div
        className={`inline-flex flex-wrap items-center justify-between  ${
          previcon ? "w-[calc(100%_-_50px)]" : "w-full"
        }`}
      >
        <div className="title-wrap">
          {title && (
            <h1 className="text-2xl text-neutral-500 font-semibold">{title}</h1>
          )}

          {path && (
            <ul className="breadcrumb list-none flex items-center gap-1">
              {path
                .filter((path) => path?.label !== "")
                .map((segment) => (
                  <>
                    {segment.label === "" ? undefined : (
                      <li
                        key={segment?.id}
                        className="breadcrumb-item font-normal text-sm inline-flex after:content-['/'] after:block last:after:content-none after:ml-1 after:text-gray-500"
                      >
                        {(() => {
                          switch (true) {
                            case segment?.id !== path.length - 1:
                              return (
                                <a
                                  href={segment?.link ?? "/"}
                                  className="text-gray-500"
                                >
                                  {segment?.label}
                                </a>
                              );
                            default:
                              return (
                                <span className="text-gray-800">
                                  {segment?.label}
                                </span>
                              );
                          }
                        })()}
                      </li>
                    )}
                  </>
                ))}
            </ul>
          )}
        </div>
        {children && (
          <div className="other-accessories ml-auto flex flex-wrap gap-4">
            {children}
          </div>
        )}
      </div>
    </div>
  );
};

export default Breadcrumbs;

Enter fullscreen mode Exit fullscreen mode

This component accepts title , breadcrumb paths , prevIcon, children and onClick event to trigger prevIcon Icon.

Inside Breacrumb.stories.tsx, add :

import type { Meta, StoryObj } from "@storybook/react";
import React from "react";
import { fn } from "@storybook/test";
import Breadcrumbs from "../Breadcrumb";

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction

const path = [
  {
    id: 0,
    link: "/",
    label: "Home",
  },
  {
    id: 1,
    link: "/news",
    label: "News",
  },
  {
    id: 2,
    link: "/nepal-win-the-race",
    label: "Nepal win the race",
  },
];
const PrevPageSvg = () => (
  <svg
    width="20"
    height="20"
    className="cursor-pointer"
    viewBox="0 0 20 20"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path
      d="M17.5005 10.0003C17.5005 10.1661 17.4346 10.3251 17.3174 10.4423C17.2002 10.5595 17.0413 10.6253 16.8755 10.6253H4.63409L9.19268 15.1832C9.25075 15.2412 9.29681 15.3102 9.32824 15.386C9.35967 15.4619 9.37584 15.5432 9.37584 15.6253C9.37584 15.7075 9.35967 15.7888 9.32824 15.8647C9.29681 15.9405 9.25075 16.0095 9.19268 16.0675C9.13461 16.1256 9.06567 16.1717 8.9898 16.2031C8.91393 16.2345 8.83261 16.2507 8.75049 16.2507C8.66837 16.2507 8.58705 16.2345 8.51118 16.2031C8.43531 16.1717 8.36637 16.1256 8.3083 16.0675L2.6833 10.4425C2.62519 10.3845 2.57909 10.3156 2.54764 10.2397C2.51619 10.1638 2.5 10.0825 2.5 10.0003C2.5 9.91821 2.51619 9.83688 2.54764 9.76101C2.57909 9.68514 2.62519 9.61621 2.6833 9.55816L8.3083 3.93316C8.42558 3.81588 8.58464 3.75 8.75049 3.75C8.91634 3.75 9.0754 3.81588 9.19268 3.93316C9.30996 4.05044 9.37584 4.2095 9.37584 4.37535C9.37584 4.5412 9.30996 4.70026 9.19268 4.81753L4.63409 9.37535H16.8755C17.0413 9.37535 17.2002 9.4412 17.3174 9.55841C17.4346 9.67562 17.5005 9.83459 17.5005 10.0003Z"
      fill="#121212"
    />
  </svg>
);
const meta: Meta = {
  title: "components/BreadCrumb",
  component: Breadcrumbs,
  tags: ["autodocs"],
} satisfies Meta<typeof Breadcrumbs>;

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

export const Primary: Story = {
  args: {
    title: "Breadcrumb Page",
    path: path,
    children: <div>add anything here</div>,
    previcon: <PrevPageSvg />,
    onClick: fn(),
  },
};

Enter fullscreen mode Exit fullscreen mode

This file defines stories of Breacrumb from where we can implement the path of the pages with title along with prev button and the children , where we can add anything we need more .

## Running Storybook

To run storybook, add following code :

npm run storybook
Enter fullscreen mode Exit fullscreen mode

This will start storybook development server. Now, You can see the Breadcrumb component in the Storybook interface.

Image description

## Conclusion

We have now successfully created a react ui using storybook, typescript, tailwind using vite.

Now we can use this stories and component on your projects, share with others.

From this we came to know that , storybook provides a solid foundation for building scalable and maintainable ui component in react.

Top comments (1)

Collapse
 
connectaryal profile image
Shiva Aryal

Nice article