Table of contents
- Initialise Project Structure
- Install dependencies
- Setup Components and StoryBook
- Setup a NextJS project
- Add the Component to NextJS
- Additional Scripts for Convenience
Monorepo's have been gaining traction over the years. Having worked with them for over a year in production environments, I can say one thing: they beat git submodules!
A typical monorepo will contain multiple packages and projects that are closely related. We can work more efficiently using a monorepo in a way that allows us to move away from:
- initializing git submodules (a repo within a repo)
- linking local packages from other locations on the disk that must be updated and installed.
workspaces, which effectively allows for a monorepo structure. You'll need to use NPM v7 and above to set up a
Recently, I embarked on building a suite of components for a NextJS project. I wanted to ensure that I could actively develop components (facilitated by Storybook) so that my NextJS projects could consume those components.
Historically, NextJS projects have always supported a
components folder. However, NextJS restricts this to the application itself. I aim to treat my component library like any other 3rd-party library I'd want to import into my projects. An architecture of this manner is easily achievable using a mono repo.
Initialise Project Structure
Our first step is to set up a project structure supporting a monorepo. Create a folder where your monorepo can live:
Great. Our next step is to create a
package.json file. We can bootstrap one quickly by running the following:
That should produce the following output:
Let's add some items to our
package.json before installing dependencies. We'll need to add a
workspaces key. Doing so informs NPM where our projects and packages sit.
NPM comes with a nifty command for adding workspaces; run the below commands and follow the instruction wizard that each command outputs:
Executing the above will run
npm install for you as well.
At this point, we will see our first fundamental differences between a traditional repo and a monorepo:
package.json exists in the root and within every "workspace." You may notice that there is only one occurrence of
package-lock.json. Now, all workspace dependencies get managed from the root folder.
Next up, we'll look into how to install workspace-specific dependencies.
When installing package or project dependencies, we can still leverage
npm install - however, this needs to be run from the root folder and requires that we pass a
-w flag to the command. Let's run through installing NextJS.
Run the command above from the
root of your project folder, where
node_modules and the
package-lock.json can be found. The only thing to call out here is that the value passed to
-w must match the
name field of one of your
packages package.json. In this example, the
name field of
If all was successful, you should see the dependencies added to
Repeat this process for any necessary dependencies in
packages/component-library as well.
Setup Components and StoryBook
Please note that this is not a tutorial on how to get set up with Storybook.
Let's take some steps to get Storybook set up correctly. We'll also create a dummy component to verify that our setup works. As StoryBook isn't a dependency of either
projects, we can install it in the root of our monorepo:
Let's also add the build commands to the root monorepo package.json:
Next, we'll need to create the
.storybook folder; let's do this and add it to the root of the monorepo. You'll want to add two files to this folder:
main.js is where we configure StoryBook's settings:
main.js file above - we tell StoryBook where to look for component stories. We also include the
@storybook/addon-postcss plugin that supports PostCSS. The extra options you see listed above enabled PostCSS Modules. The last two keys:
core, allow us to configure StoryBook to use Webpack v5.
preview.js, add the following:
preview.js file allows us to customize the appearance of stories without affecting the component logic itself. Hence, the name "decorator". I've just added some basic styling here to center the button.
Next, we need to flesh out our
Button component. In
package/component-library, add a new folder called
Button; in that folder, add the following files:
- Button.module.css - handles the styling of the button
- Button.stories.jsx - handles the button story
- Button.jsx - the button component itself
You can find the contents of these files in the repo. Great, we need to see if we can import our component into NextJS.
Setup a NextJS project
We need to return to NextJS quickly to tie everything together meaningfully. First, add a
pages folder to the NextJS site and the scripts needed in
package.json to build NextJS.
To add the scripts, go to
projects/monorepo-site/package.json and replace the current
scripts field with:
pages folder, add an
index.js file with the following contents:
Add the Component to NextJS
Now, it's time to import our component into NextJS. Let's import the button into
When you run
node_modules or any other local folder outside its reach.
To get around this problem, configure a property in
Note that the
component-library, is passed, not the path to the folder on the disk. You'll need to restart your dev server.
Your custom Button component now lives in NextJS and StoryBook and can be iterated upon and tracked in the repo!
You may run into trouble with NextJS asking for React v18 if you use NextJS 13. This tutorial was written using NextJS v12 and React v17.0.2
Additional Scripts for Convenience
To run our build commands, we'd have to
cd into every
package. Doing so can be time-consuming, so let's add the following
scripts to the
package.json found in the root:
Let's run through these two commands as they differ slightly. Running
npm run build in the root will allow us to fire off individual
build scripts in all workspaces. If the
build task isn't in a
project package.json, it will simply skip it.
If, at some point, this particular example scaled to have three production sites, all with a
build command, we could run
npm run build from the root, and for each workspace, the build task will execute. Pretty nifty!
npm run dev:monorepo-site is slightly different - this command targets one workspace and fires off the
script defined within that workspace (as long as it exists). Doing so allows us to spin up the
dev server for
monorepo-site from the root without navigating to the folder.
Let's review where we're at:
- We initialized a monorepo structure using
- Set up a component library and production site with StoryBook and NextJS.
- Added scripts that will make our developer experience more accessible to manage.
- All our dependencies get managed from the top level, and we do not have to pull in other repos or git submodules.
At this point, we could easily add a new site project or extend our component library. We could also add other
packages that may deal with custom server functionality, CMS integrations, or API support. The possibilities are endless. Now it's time for you to try!