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:
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
Top comments (0)