loading...

Using Storybook in a Monorepo

kamranayub profile image Kamran Ayub Originally published at kamranicus.com ・3 min read

I'm currently working in a small monorepo and we have multiple packages set up for shared components, like this:

packages/
  atoms/
  molecules/
  organisms/

Each package has React components under a src directory and since each directory is a package, they also contain node_modules (more on why that's important below).

packages/
  atoms/
    node_modules/
    src/
      components/
        Hello.js
        Hello.stories.js
    package.json
  molecules/
  organisms/

This is different than other posts I've seen that use a root stories/ directory -- that is not how we'd like to set up our repo, we'd prefer stories to live right next to the components they describe.

Setting Up Storybook

You can follow the same steps on the getting started page for setting up Storybook in a monorepo.

Once it's done, you should have a new folder at the root:

.storybook/
  config.js

If not, make sure that gets created. We need to make some changes to config.js to read stories within each package in the monorepo.

Configuring Storybook for a Monorepo

The key issue I ran into that prompted me to write about this is that we need to import stories using the Webpack context:

require.context('../packages', true, /stories.jsx?$/);

This prompts Webpack to scan a src directory for paths containing stories.js or stories.jsx anywhere in the string.

Since Webpack statically analyzes this code it means we cannot dynamically read the file system and iterate through each package directory (I tried that 😔).

There's another problem. Remember I mentioned each package directory has node_modules which means that the following paths will match the regular expression:

./atoms/node_modules/@storybook__react/src/stories/blah.stories.js
./molecules/lcov-report/_html/src/components/Hello.stories.js

And you can imagine more paths like that can match. Webpack will attempt to import these files into the bundle it generates but these will most likely cause build errors. That isn't what we want! We need to exclude everything but the src directory in a package when matching files.

The solution, as it turns out is fairly straightforward, since we have a convention where each package has components within a src directory, we really just need to match that inner src after the package name, so we can write a regular expression like this:

require.context('../packages', true, /^\.\/[^\/]+\/src\/.*stories\.jsx?$/);

Let's break it down:

^               # match beginning of path
\.\/            # the path will begin with a "./", like ./atoms
[^\/]+          # get the first path segment (match characters up to first /)
\/src\/         # ensure we match under the `src` directory
.*              # match any character
stories\.jsx?   # match anything with "stories.js(x)" in it
$               # match end of string

Here's an example using my favorite regex tool RegExr on which paths match and which don't:

Regular expression match example

Handy!

The Final Config

Here's our final config.js:

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

function loadStories() {
  const req = require.context('../packages', true, /^\.\/[^\/]+\/src\/.*stories\.jsx?$/);
  req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);

I hope this helps someone else and saves them the few hours I spent performing the correct rites to get this to work!


If you thought this was helpful, you can follow me or subscribe to my blog!

Originally posted at Kamranicus

Posted on by:

kamranayub profile

Kamran Ayub

@kamranayub

Kamran Ayub is a technologist, speaker, and Pluralsight author and specializes in building full-stack solutions with a focus on modern web technology and cloud native architecture.

Discussion

markdown guide