Create React component libraries with Storybook and styled-jsx.

Creating a NPM module we can import on any app or project to include a predefined set of React components is a very good way to achieve code reusability. If you are creating a visual component library or a state management container that you want to include on several apps making an NPM module out of it is a very good idea.

Image for post
Image for post
misschristi1972 @ Flickr

My main to-go choices of helper libraries to do this where: nwb (https://github.com/insin/nwb) & react-cdk (https://github.com/storybooks/react-cdk) Both good alternatives, but for the purpose of this article I will build one component library module from scratch. Of course, we will need some help.

To create the module lets make a folder “awesome-components” with a package.json file in it first.

We will get a standard initial package.json definition.

Since we are creating a module we should use the peerDependencies section to specify what packages this library will need from the host application. In this case react (https://facebook.github.io/react/) and … styled-jsx (https://github.com/zeit/styled-jsx) for CSS-in-JS. Peer dependencies means that once out module is installed, it will use the dependencies packages from the host application and not try to install copies of them for himself.

The other modules we need are: babel (cli, core, presets, plugins, etc) (https://babeljs.io/) to transpile all the React JSX code we will have and any other ES6 goodies that will make our task more fun. We also need, react, react-dom and styled-jsx added as dev dependencies. so…

After this, our package.json file might look something as this:

With the development dependencies in place, we need a simple build script to take our source code on ES6 with React JSX and transpile it to a more universal JavaScript version, that can then be imported on any app. This a simple one-liner.

Our source code will reside on a “src” folder and our built code will go into a “dist” folder.

With babel we need to specify a .babelrc file containing babel presets, plugins, etc we want to use. Usually you don’t need to specify a per-environment config but, we will here due to some little problems with styled-jsx that gets solved by having different configuration for building the module on dev and building for production/publishing.

And… we are ready to start creating some components.

Lets create a reusable button in a src/index.js file

There is nothing special about this button component. It has a couple props: title and onClick, it will show a button HTML tag, with a onClick handler, a title and some styling (blue background, white text, some padding, no border)

To build our code:

Notice the build script will transpile everything under the “src” folder, outputting to “dist” folder. And that’s pretty much it. At this point we have a working component library we can publish to a public/private NPM registry or simply use from Github directly (luckily yarn and npm support installing from git repos)

Be sure to have your npmjs.com credentials already logged.

Before doing this we will need a couple more essential files: .gitignore and .npmignore. The first allow us to avoid having files we don’t need on git. In this category we have the full node_modules folder. .npmignore on the other hand allow us to tell npm (or yarn) which files the final user of our module won’t need. Like “src” folder, .babelrc, test folders (if any), etc.

Oh yes, awesome-components@0.1.0 (at this point of time) is published on NPM (https://www.npmjs.com/package/awesome-components)

But, publishing out module on NPM to be able to see how the component looks is a slow process. Not very productive. We need some way to be able to showcase our components in development time. Enter Storybook (https://storybook.js.org/)

Is a amazing tool, in the authors words Storybook is:

“The UI Development Environment You’ll ♥️ to use”

I know I love it.

Storybook will allow us to speed development of out components by adding an UI environment where we can showcase our components uses, different properties, all, in a fancy web UI, with hot-reloading and several other plugins that help us build faster better components.

Lets add a storybook to our module. We need first to install the storybook CLI package.

We then run the right command on our module root folder.

This command will detect we are building a React component library, add the right devDependencies and 2 scripts to package.json.

Also, 2 folders are added to the project. A .storybook folder with two files: config.js and addons.js. This are basically configurations for the Storybook. We won’t need in the scope of this post to change any of this files, but, I recommend you take a look was inside and how to make changes to your storybook.

The other folder is more interesting, is the stories folder. Stories in the Storybook context are basically hierarchically predefined views of components with different props, constructs, etc. By default will come up with a couple of examples of views. We will however modified to use our own Button component.

Notice we are using now our own Button component from the “src” folder.

To run the Storybook we do:

And open http://localhost:6006/

Image for post
Image for post
Storybook

We can keep adding components to our source code and at the same time create stories for them so we can visually see how are they looking on the Storybook, then, build and publish.

Styled-jsx is a simple and elegant solution to implement CSS-in-JS. Works really fine and is tightly integrated to my favorite React framework for server rendered applications Next.js (https://zeit.co/blog/next)

Is really nice to include a component library and not have to worry about styling, that otherwise we would have to track down and include on a build pipeline. This way only components actually on the page get their CSS transpiled and included on the page, even in SSR.

Of course, this is really important. Being only visual components IMHO simple snapshot testing might suffice. Luckily for us, the team at Storybook solved this problem already via a plugin StoryShots (https://github.com/storybooks/storybook/tree/master/addons/storyshots). We will need to install this plugin as a dev-dependency first. (we also need jest and react-test-renderer)

Add a __test__/Snapshot.js file with the following content:

A “test” script on package.json:

And a “test” section on our .babelrc file (identical to the development section):

When we run the tests the first time:

Since is the first time, snapshots will be created for all the Storybook stories we have so far and automatically make those tests pass.

If we change any of our components, HTML or CSS styling, test will fail. Lets try it:

Notice the change to a red background-color.

Tests again.

In other words, our tests fails, in this case because we changed the styling. But what if we want to keep that new changes to styling? To update the snapshots we can:

This will run the snapshot testing rebuilding the snapshots and making all tests pass again.

Test coverage

Luckily (again) for us, Jest (https://facebook.github.io/jest/) includes support for generating coverage reports. We only need to add the parameter to the jest CLI.

But first, lets change our Button component a little bit. Lets say the component will show a red button, instead of a blue one, if the title starts or ends with and “!” character.

Since we changed our styling and our HTML tags, the snapshots that we had will make this fail. We will run test updating the snapshots.

Image for post
Image for post

The tests pass, but as you can see, coverage drop to a ~75% and we are not covering lines 5 and 9. Exactly the lines that returns true to the condition we expect to make the Button have a different background color.

So, lets add a couple of stories to cover this 2 cases.

And run tests again:

Back to 100% again.

What else we can do? A lot. Add a README file to your module, or even better build an static version of your module’s storybook and publish it to the module repo’s own Github page.

Also, take a look at all the amazing plugins Storybook provides (project/community)

Use a linter, standard (https://www.npmjs.com/package/standard) or xo (https://www.npmjs.com/package/xo)

And always use (and respect) semver (http://semver.org/) for your modules.

Software Engineer @ Coalition Inc., Miami FL

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store