loading...
Cover image for Documenting React Components With Storybook

Documenting React Components With Storybook

emmabostian profile image Emma Bostian ✨ ・5 min read

What Is Storybook?

Storybook markets itself as a playground for UI components and its main focus is on “writing stories.”

Storybook uses the concept of stories to document components.

A story usually contains a single state of one component, almost like a visual test case. Technically a story is a function that returns something that can be rendered to a the screen.

Your component storybook will contain many different stories for many different components.

Each story we write will contain a single state, for example:

Button
  ├── primary
  ├── secondary
  └── tertiary

What’s great about Storybook is that it works with many popular front-end frameworks and libraries such as React, Vue, React Native, Angular, and more.

Set Up

For this tutorial, I’ll be adding Storybook to my Building Design Systems With React talk which I gave in May 2019 at ReactJS Girls London. You’re welcome to follow along with your own code, or check out the final code in my repository.

  1. Change into your project directory and install your dependencies (if you need to). I forgot and spent ten minutes trying to figure out why nothing was working…

Then, install the boilerplate files for Storybook. (This will take a hot second to download. In the meantime, perhaps you’d like to brew some fresh coffee.)

cd my-project
npx -p @storybook/cli sb init

This tool will check out your package.json file to determine which framework or library (view layer) you’re using. If automatic detection fails, or if you want to use Storybook for HTML, use the following command:

npx -p @storybook/cli sb init --type html | <other-type>
  1. Once installed, let’s start Storybook with the following command:
npm run storybook

After running, a localhost window popped up in my browser and I saw this screen:

Storybook

  1. Now we’ll want to add Storybook as a development dependency within our project. We can do that by running the following command:
npm install @storybook/react --save-dev
  1. Storybook has a few peer dependencies which we also need to have installed. react and react-dom should be saved as normal dependencies. @babel/core and babel-loader should be saved as development dependencies.
npm install react react-dom --save
npm install babel-loader @babel/core --save-dev
  1. We’ll want to add an npm script so we can easily start Storybook. Inside our package.json file, let’s add a storybook script.
{
  "scripts": {
    "storybook": "start-storybook"
  }
}
  1. Lastly, let’s create the Storybook config file, which will simply tell Storybook where we’ll be writing our stories.

You most likely already have this file created from the previous steps, however if you don’t, create a new config.js file inside the storybook/ folder.

My config.js file contains the following:

import { configure } from "@storybook/react";

function loadStories() {
  require("../src/stories");
}

configure(loadStories, module);

This tells Storybook to look inside of the src/stories folder for our stories.

Let’s Document

  1. Ensure your development server is running with npm run storybook.
  2. First, we’ll get rid of the boilerplate inside of src/stories/index.js. My file looks like this:
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
  1. Now, let’s import our first component. For my project, I’ll be importing my Button component. It lives directly inside of the src/ folder.
import Button from '../Button';

I’m using enums to describe my button types, so I’ll import those as well.

import { ButtonTypes } from "../buttonTypes";
  1. We now want to write our first storiesOf for our button. We’ll start with three states: primary, secondary, and tertiary.

We need to use the .add() function to add each state. This takes two arguments: the name of the state we want to add and a function which returns the component.

Here is what my index.js file looks like:

import React from "react";

import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";

import Button from "../Button";
import { ButtonTypes } from "../buttonTypes";

storiesOf("Button", module)
  .add("primary", () => (
    <Button
      type={ButtonTypes.PRIMARY}
      onClick={action("clicked")}
      label="Primary"
    />
  ))
  .add("secondary", () => (
    <Button
      type={ButtonTypes.SECONDARY}
      onClick={action("clicked")}
      label="Secondary"
    />
  ))
  .add("tertiary", () => (
    <Button
      type={ButtonTypes.TERTIARY}
      onClick={action("clicked")}
      label="Tertiary"
    />
));

When we check this out in the UI, we should see one story, Button, with three states: primary, secondary, and tertiary.

Storybook

  1. Now that this is working, I want to modularize my stories a bit better. If I were doing this for an enterprise application, or a full design system, I’d add the stories next to the components themselves. However, due to the fact that this is a proof-of-concept, I’ll be adding them within the stories/ folder.

I’ll create a buttonStories.js file inside of src/stories/.

Next, I’ll copy and paste all of the code from the index.js file over to this new file.

Lastly, I’ll update index.js to import the buttonStories.js file.

import "./buttonStories";

And that’s it! You now can create Storybook stories to document the states of your component.

Theming

You can select different themes for your Storybook documentation.

Dark Theme

  1. Import addParameters and themes:
import { addParameters } from '@storybook/react';
import { themes } from '@storybook/theming';
  1. Next, add the theme key to the parameter options:
import { addParameters } from '@storybook/react';
import { themes } from '@storybook/theming';

// Option defaults.
addParameters({
  options: {
    theme: themes.dark,
  },
});

And voila, a dark theme!

Dark theme

Custom Themes

You can generate a custom theme by using the create() function.

  1. Create a new file within the .storybook folder, and name it appropriately for your theme. I’ll call mine purpleDream.js

  2. Paste the following code and update the values to suit your theme’s needs.

import { create } from "@storybook/theming";

export default create({
  base: "dark",

  colorPrimary: "mistyrose",
  colorSecondary: "purple",

  // UI
  appBg: "#9f84bd",
  appContentBg: "#ede3e9",
  appBorderColor: "grey",
  appBorderRadius: 4,

  // Typography
  fontBase: '"Open Sans", sans-serif',
  fontCode: "monospace",

  // Text colors
  textColor: "white",
  textInverseColor: "rgba(255,255,255,0.9)",

  // Toolbar default and active colors
  barTextColor: "white",
  barSelectedColor: "white",
  barBg: "#ca7df9",

  // Form colors
  inputBg: "white",
  inputBorder: "silver",
  inputTextColor: "white",
  inputBorderRadius: 4,

  brandTitle: "My custom storybook",
  brandUrl: "https://example.com",
  brandImage: "https://placehold.it/350x150"
});
  1. Update your config.js file to use your new theme.
import { configure } from "@storybook/react";
import { addParameters } from "@storybook/react";
import purpleDream from "./purpleDream";

function loadStories() {
  require("../src/stories");
}

addParameters({
  options: {
    theme: purpleDream
  }
});

configure(loadStories, module);

And there you go. You now have a custom theme (hopefully not as ugly as mine.)

Purple dream


I hope you enjoyed this tutorial on getting started with Storybook & React. Feel free to check out my code on GitHub.

Discussion

pic
Editor guide
Collapse
michaeltharrington profile image
Michael Tharrington (he/him)

I'm admittedly not the most technical person here 😅 but it's possible that I caught a typo:

"Storybook uses the concept of stories to document complements."

Is complements meant to be components there?

Collapse
emmabostian profile image
Collapse
eerk profile image
eerk

I still find it hard to wrap my head around the storybooks concept.... what's the difference with just writing React components to test out a UI?

And what if I'm happy with my storybook... can I export it as a React app?

Collapse
murkrage profile image
Mike Ekkel

Bit late to the party here, but I don't think it's about being able to export it as a React app but more to document your components. Knowing what components you have and how they behave is invaluable as it will reduce the time you spend re-inventing the wheel you invented a while ago with some other component.

Collapse
eerk profile image
eerk

So it's just for testing out components, and when they work, then you... copy > paste the code?

Thread Thread
murkrage profile image
Mike Ekkel

I have yet to test it out this far. I'm probably going to dabble around with Storybook this weekend as I want to suggest it to my team. I recently joined the team but there is no design system in place, nor are any of the components documented which makes the codebase a bit of a mess. I'm trying to turn the tide and looking for ways to help us keep track of our components, how we use them and what we can customise. Storybook seems like a good choice, but I'll have to give it a whirl before I know for sure. I'll write about my experience with it and will include a way to 'export' it to your own codebase if I find one :)

Thread Thread
eerk profile image
eerk

Haha, great! Looking forward to the post. So many things to learn and try out.

Collapse
axelledrouge profile image
AxelleDRouge

I have just started working with storybook on a project that I have designed, with Adobe XD. I love the process with the Component Driven Development. It feels more clean and swift.I know what I want and to how to get it component by component. love it <3

Collapse
elizabethschafer profile image
Elizabeth Schafer

I love Storybook! It's so much easier building and testing components in isolation.

Collapse
tirthaguha profile image
Tirtha Guha

Hi,

Are there any alternatives of storybook. I find storybooks a little un-customisable, if I have to host my component library for demo purpose. I want to have a different home page and component listing, may be some custom tags like experimental, under development, release versions etc against each component.

Collapse
mccabiles profile image
Miguel

Thank you for this article! I have dabbled into Storybook with Vue a few days ago and was considering writing an article on my experience as well, and your post has inspired me to do so. Cheers! :)

Collapse
alex_barashkov profile image
Alex Barashkov

Thanks for the article. Exactly wanted this sort of guide to throw it to devs who don't familiar with a storybook concept.

Collapse
equinusocio profile image
Mattia Astorino

Awesome! I also use the plugin storybook-knobs to set states in a single story and storybook-addon-a11y to run basic a11y tests.

Collapse
anfossistudio profile image
AnfossiStudio

Awesome Article Thank you

Collapse
christopherkade profile image
Christopher Kade

Thanks for the article ! It pushed me to start writing a component library just for the heck of it 😍, the development experience is extremely smooth so far

Collapse
pgangwani profile image
pgangwani

Awesome write up

Collapse
ecleptic profile image
Cameron Green

Ahh. I struggled way too long with themeing my storybook!
Thanks!

Collapse
nampdn profile image
Nam Pham

I’m struggling with setting up React TypeScript with the react-native-package, it could not build using webpack tweak. Do you have any skill in this topic?

Collapse
leoyli profile image
Leo Y. Li

You may want to join our discord here: discordapp.com/invite/UUt2PJb. But this should be easy to do using Babel 7 by putting this extending webpack under .storybook folder:

const webpack = require('webpack');

module.exports = async ({ config }) => {
 // typescript supports
 config.module.rules[0] = { ...config.module.rules[0], test: /\.(mjs|jsx?|tsx?)$/ };
 config.resolve.extensions = [...config.resolve.extensions, '.ts', '.tsx'];

 return config;
};