Writing a shared React component library with storybook

At GumGum, my team builds and works on many React JS web applications. As the number of apps grew, we found ourselves writing the same code and components for each app. This is not ideal because we had to spend time writing similar code, and it was not centralized, so each variation might have slight differences.

Michele
GumGum Tech Blog
5 min readAug 3, 2017

--

A few things came together at once for our team:

  1. Our UI team created a design system that allowed us to use the same UI styles across all apps.
  2. We liked the concept of our BEM style library, but found it verbose to add so many class names on everything.
  3. We kept getting more projects quickly, so we needed to streamline our development process for fast prototyping.
  4. My team discovered React Storybook, a development environment for React components.

It all made sense — let’s create a shared component library!

  • We can import these components into each project.
  • Code is only developed one time in one place.
  • We can abstract the lengthy class names away.
  • We can use Storybook to self document our library and provide examples for use.
  • As a team, we can all contribute to this project and develop best practices. And the response has been great! We’re even working across teams to build this together.

Our component library

The first thing we need here is the library. In a new project, we made a components folder and started with our most basic components.

Here is a stripped down component example. Our badge takes in text to display in the badge and context, which is the color applied to the badge via our design system. We also apply some standard props to every component: className, style, and …otherProps, which allows you to pass in any other classes, styles, or props that you want — making your component very flexible.

import React, { PropTypes } from ‘react’;const Badge = ({
text,
context,
className,
style,
...otherProps
}) => {
const classes = `${context} ${className}`;
return (
<span
className={ classes }
style={ style }
{ ...otherProps }
>
{ text }
</span>
);
};
Badge.defaultProps = {
text: ‘’,
context: ‘default’,
className: ‘’,
style: {}
};
Badge.propTypes = {
text: PropTypes.string,
context: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object
};
export default Badge;

Storybook

Now that we have our library, let’s set up Storybook so we can document and display our components. Storybook has great documentation and is very customizable with a variety of Addons. Out of the box, you can use Storybook as a way to show and interact with your components. We also wanted to use our Storybook to provide complete documentation for using our library, and allow users to see all of the options available via props. Therefore, we customized our Storybook with the following Addons:

  • Options — allows you to customize a few Storybook options. We changed the default name to “GumGum Storybook”, added the url, and put the addons on the right side instead of the default bottom.
  • Readme — allows you to write documentation in markdown format as a readme file. We used to have all of our documentation in the storybook files, but were limited because not all markdown was supported. Our files also started getting long and disorganized. Keeping all documentation in its own markdown file makes it really easy to write the documentation. We now organize our storybook with a folder for each component, containing a README.mdand index.js:
/_stories    /Badge       index.js       README.md    /Button       index.js       README.md
  • Knobs — makes your props into actionable items. For example, in our Badge component, we have a “context” prop. There are a number of options, including [“default”, “primary”, “secondary”, “success”, “info”, “warning”, “danger”]. By using knobs, we can pass all of these options to our Storybook and the user sees a select that allows them to dynamically toggle any of the options. This is really powerful because someone can play with the component and see all of the options without needing to build anything.
  • Info — displays your story source and displays the component’s propTypes and defaultProps. In conjunction with “Knobs”, this gives the user the ability to change the props to what they need and then copy the story source into their project.
  • Actions — Some of your components may be more complex and track actions. This Addon shows the user when an action is taken, so you know the component is properly hooked up.

To start, run this command in your project and then follow the steps on Storybook docs:

yarn i --save-dev @storybook/react

Storybook also provides a command to export your Storybook as a static app. This is helpful if you’d like to host it without a server.

And finally, we needed to add a link to our custom css file for our design system. This goes in .storybook/preview-head.html

Exporting your library

For our purposes, since we’re using this library internally, we only needed to be concerned about exporting ES6 components.

First, we have a single index.js file that exports each component.

You’ll need a few packages and settings depending on your set up on the projects that will import it.

Our .babelrc:

{
// whichever presets your projects use should match here
"presets": [
"es2015",
"stage-0",
"react"
],
"plugins": [
"transform-runtime", // for polyfills
"transform-react-remove-prop-types" // we use this to remove the propTypes
]
}

To handle the actual build tasks, we added a few scripts in our package.json:

“module”: “dist-es6/index”,
“prebuild”: “rimraf dist dist-es6”, // removes the /dist-es6 folder before building
“build”: “babel components — out-dir dist-es6 — source-maps inline”, // builds everything in /components/index.js to /dist-es6
“postinstall”: “postinstall-build --only-as-dependency dist”

Postinstall lets us automatically build our library as es6 modules when the library is added to a project. This way we don’t have to build the files and keep them as part of our library. To deal with dependencies on our other projects, we use postinstall-build --only-as-dependency to grab any dependencies we need to build our library and remove them after they are built.

Importing your library into other apps

Finally, we need to import our library into our other React apps to use the components.

We aren’t publishing this on NPM, so when adding the library to a project, we can use this command:

yarn add git+ssh://git@bitbucket.org/YOUR_PROJECT.git#0.

(We do releases and use versioning for this project. Highly recommended!)

And then in a component file, you can import components from your library:

import { Badge } from 'YOUR-LIBRARY';

Enjoy your new library! Special thanks to the GumGum Web Engineering team for their continuing support and contributing to this project. Check out our library at gumdrops.gumgum.com.

We’re always looking for new talent! View jobs.

Follow us: Facebook | Twitter | | Linkedin | Instagram

--

--