DEV Community

Eduardo Henrique Gris
Eduardo Henrique Gris

Posted on

Documentation of components in React with TypeScript using Storybook

Introduction

Storybook is a lib that allows, among other things, the documentation of components in isolation, parallel to the application. In this article, I will present a way to customize component documentation, which I learned during the development of a personal project that I use for study purposes. I hope this will help bring visibility to some of the options available when using this library. The following steps will be followed:

  • setup: storybook configuration
  • stories: definition of the main scenarios for the components to be documented
  • mdx: documentation of the components, incorporating the customizations defined in the stories
  • example: an example of component documentation will be presented

Each component will have its own mdx and stories file.

Setup

To add storybook to a React application with the initial configurations, you need to run the following command in the terminal: npx storybook init. This command will add storybook based on the project's dependencies. To check if the addition was successful, run yarn storybook in the terminal and see if a page opens (at this point, without any documented components).

Stories

For each component, a *.stories.tsx file will be created, where the most important customization scenarios to be documented will be defined, following this structure:

import React from "react";
import { Meta, StoryObj } from "@storybook/react";
import Component from "./Component";

const meta: Meta<typeof Component> = {
  component: Component,
  title: "Component",
  argTypes: {},
};
export default meta;

type Story = StoryObj<typeof Component>;

export const Scenario1: Story = (args) => (
  <>
    <Component {...args} />
    <Component {...args} props1="option1" />
    <Component {...args} props1="option2" />
  </>
);

Scenario1.args = {
  // (...),
  props1: "default"
};

// Scenario2
// Scenario3
Enter fullscreen mode Exit fullscreen mode

From the imports of @storybook/react:

  • Meta: define the metadata for the component to be documented. In this case, the component itself is passed in component, and the title that will appear in the sidebar is specified in title.
  • StoryObj: allows you to define the customization scenarios of the component to be displayed in the documentation, corresponding to the definitions of Scenario1, Scenario2... mentioned above.

For each defined scenario, an entry will be created in the sidebar, within the "folder" named according to the title defined in the Meta, with the following structure:

  • Component
    • Scenario 1
    • Scenario 2
    • Scenario 3

In Scenario1.args above, the default props for the component to be presented are defined, with the aim of setting a base display for the component (for example, text for a button like Button).
In const Scenario1, the component to be rendered is defined, varying props1, with the goal of showing in the documentation what happens to the component when this prop is changed.

MDX

It will be the component documentation, where the scenarios present in the *.stories.tsx file will be included, and texts will be defined to explain the main functionalities of the component, following this structure:

import { ArgTypes, Canvas, Meta, Story } from '@storybook/blocks';
import * as Stories from './Component.stories';

<Meta of={Stories} />

# Component

Component description.

## Props

<ArgTypes />

## Predefined properties

### Scenario 1

Description of the use of props1.

<Canvas withToolbar>
  <Story of={Stories.Scenario1} />
</Canvas>

## Custom properties

// ### Scenario 2

// ### Scenario 3
Enter fullscreen mode Exit fullscreen mode

It's a file that mixes markdown with javaScript. From the imports of @storybook/blocks:

  • ArgTypes: it returns a table with the names of all available props for the component, their descriptions (including the Types of the props), and whether they have a default value. This is done automatically, as the npx storybook init command already adds @storybook/addon-essentials to the app. For versions of storybook that do not include this addon by default, you will need to add the library with yarn add @storybook/addon-essentials --dev and then add it to the addons in the main.ts file located in the .storybook folder:
// ...
const config: StorybookConfig = {
  // ...
  addons: [
    // ...
    "@storybook/addon-essentials",
  ],
};
export default config;
Enter fullscreen mode Exit fullscreen mode

The table will have the following layout:

Image description

  • Canvas: this will create a box where the components from the scenario defined in the *.stories file will be displayed. It includes an option called Show code that reveals the code to use the displayed component

Image description

  • Meta: defines where the associated stories file for the documentation is located, coming from the import import * as Stories from './Component.stories';

  • Story: allows rendering the scenarios defined in the stories file, encapsulated by the Canvas to also enable viewing the code.

Example

I will present a simple example of documentation based on what has been provided above. Starting with the creation of a component defined in Tag.tsx:

import React from "react";
import styled from "styled-components";

export interface TagProps {
  text: string;
  backgroundColor?: string;
  type?: "success" | "alert" | "error";
}

export interface StyledTagProps {
  $backgroundColor?: string;
  $type?: "success" | "alert" | "error";
}

export const StyledTag = styled.div<StyledTagProps>`
  border: none;
  padding: 10px 12px;
  background-color: ${(props) =>
    props.$backgroundColor
      ? props.$backgroundColor
      : props.$type === "error"
        ? "#e97451"
        : props.$type === "alert"
          ? "#f8de7e"
          : props.$type === "success"
            ? "#50c878"
            : "#d3d3d3"};
  pointer-events: none;
  border-radius: 5px;
  width: fit-content;
`;

const Tag = ({
  text,
  backgroundColor,
  type,
}: TagProps) => (
  <StyledTag
    $type={type}
    $backgroundColor={backgroundColor}
  >
    <span>{text}</span>
  </StyledTag>
);

export default Tag;
Enter fullscreen mode Exit fullscreen mode

It is a Tag component with a predefined type property, meaning that the app itself defines the associated css based on the given type. The possible values are error, alert and success. It also has a customizable backgroundColor property, allowing users to choose any color they want.
In the example, the css is defined using a lib called styled-components, but the documentation is indifferent to how the css is defined. Types are defined for both the component's props and the related to styled component.

Now, a specific component for documentation, StorybookContainer, will be created. This component will be responsible for centering the scenario components inside the Canvas of the mdx file, with a small spacement between them. In the file StorybookContainer.tsx:

import React from "react";
import styled from "styled-components";

export interface StorybookContainerProps {
  children: React.ReactNode;
}

export const StyledStorybookContainer = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
`;

const StorybookContainer = ({ children }: StorybookContainerProps) => (
  <StyledStorybookContainer>
    {children}
  </StyledStorybookContainer>
);

export default StorybookContainer;
Enter fullscreen mode Exit fullscreen mode

The file Tag.stories.tsx will be created with the two scenarios to be presented in the documentation:

import React from "react";
import { Meta, StoryObj } from "@storybook/react";
import Tag from "./Tag";
import StorybookContainer from "../StorybookContainer/StorybookContainer";

const meta: Meta<typeof Tag> = {
  component: Tag,
  title: "Tag",
  argTypes: {},
};
export default meta;

type Story = StoryObj<typeof Tag>;

export const PredefinedType: Story = (args) => (
  <StorybookContainer>
    <Tag {...args} />
    <Tag {...args} text="Success" type="success" />
    <Tag {...args} text="Alert" type="alert" />
    <Tag {...args} text="Error" type="error" />
  </StorybookContainer>
);

PredefinedType.args = {
  text: "Default",
};

export const BackgroundColor: Story = (args) => (
  <StorybookContainer>
    <Tag {...args} />
    <Tag {...args} backgroundColor="#89cff0" />
    <Tag {...args} backgroundColor="#f0aa89" />
  </StorybookContainer>
);

BackgroundColor.args = {
  text: "Tag",
  backgroundColor: "#d3d3d3",
};
Enter fullscreen mode Exit fullscreen mode

Two scenarios were defined in the file to show how the component changes with variations in the type props (which is a predefined property in the app) and how the component changes with variations in the backgroundColor props (which is customizable, as the user can specify any color they want to use).

Finally, the documentation file for the Tag component, Tag.mdx, will be created, where the two scenarios defined in the stories file will be displayed:

import { ArgTypes, Canvas, Meta, Story } from '@storybook/blocks';
import * as Stories from './Tag.stories';

<Meta of={Stories} />

# Tag

Tag base component.

## Props

<ArgTypes />

## Predefined properties

### Type

There are three type predefined properties: success, alert and error.

<Canvas withToolbar>
  <Story of={Stories.PredefinedType} />
</Canvas>

## Custom properties

### BackgroundColor

Tag backgroundColor can be modified.

<Canvas withToolbar>
  <Story of={Stories.BackgroundColor} />
</Canvas>
Enter fullscreen mode Exit fullscreen mode

It will describe the component being displayed, with a table showing its props due to the inclusion of . It will also shows a customization scenario using the variation of the type props (a predefined property) and a scenario using the variation of the backgroundColor props (a customizable property).

Finally, run yarn storybook in the terminal. The sidebar will look like this:

Image description

And the documentation will look like this:

Image description

And clicking on Show code in one of the scenarios displays the associated code:

Image description

Conclusion

The idea of this article was to introduce a component documentation lib that allows customizing documentation using an mdx file in various ways, utilizing the blocks provided by the lib. A way for writing this documentation file was presented, but given the many possibilities offered by the lib, I will include some links in the references for those who want to delve deeper into the subject.

References

Stories File
Blocks
Argtypes Block
Canvas Block
Meta Block
Stories Block
MDX File

Top comments (0)