Adding a React Components Package to a Monorepo
Tags: react, javascript, webdev, github
Published: 2021-12-24 11:19:45

In this post I will be adding a React components package to an existing monorepo (along with Storybook) and automatically publishing it to NPM using GitHub actions, with a somewhat _disturbing_ surprise at the end ;) 
The monorepo is my “Pedalboard” monorepo which is described in more detail in [this post](https://dev.to/mbarzeev/no-bs-monorepo-part-1-3c3a).

The component I chose to add is my worn down, truly nothing to write back home about, **Pagination** component, but hey - still a React component :) let’s get started - 

> I will do this manually though I recommend that if your monorepo is supposed to be used by many developers and hold many components, you should have a sort of a package generator for it, so that the packages created are aligned and the dev experience is good. 

I will start with adding a “components” directory under the “packages” directory, and in it an “src” directory.
Then I will cd that directory and initialize yarn like so - 

```bash
cd packages/components && yarn init
```

After the initial package.json was created, I will change its name to include the `@pedalboard` namespace, add Jest and Eslint as dev dependencies and mark its publish config as “public” (so it will not inherit the root project’s “private” configuration). Here is how my package.json looks like now:

```json
{
   "name": "@pedalboard/components",
   "version": "0.0.0",
   "description": "A set of well-crafted components",
   "main": "index.js",
   "author": "Matti Bar-Zeev",
   "license": "MIT",
   "publishConfig": {
       "access": "public"
   },
   "scripts": {
       "test": "echo \"Still no test specified\" && exit 0",
       "lint": "eslint ./src"
   },
   "devDependencies": {
       "eslint": "^8.4.1",
       "jest": "^27.4.3"
   }
}
```
<small>(We are still not running any tests so the yarn test script is does nothing)</small>

I will also create an “index.js” file in the root directory of the package which will act as the export barrel for all the future components this package will have.

As mentioned, the component is the Pagination component, which is a simple component for pagination (You can read more details about it in [a previous post](https://dev.to/mbarzeev/from-render-props-to-hooks-mkc), where I migrated it from render-props to Hooks). 
It is a React component so we start with fetching React and ReactDOM packages as dependencies, but these are _peer dependencies_ since we assume that whomever will use this components package will do it from a project which already has them.

```bash
Yarn add react react-dom -P
```

Now I will add my component’s source code to the Pagination directory (you can check the [GitHub repo for the source code](https://github.com/mbarzeev/pedalboard/tree/master/packages/components/src/Pagination)), but while we have the entire logic for the component we still don’t have any means to render it on screen. For this purpose I would like to have Storybook for my components - let’s get that working for us:

Following [Storybook’s doc](https://storybook.js.org/docs/react/get-started/install)s I will init it on the components package - 

```bash
npx sb init
```

Right… all of the Storybook dependencies were hoisted to the root project’s node_modules (we’re using Yarn Workspaces, remember?), and I got a new “stories” directory under the “src” directory of the components package.
In addition to that, the init process added 2 new npm scripts to my package.json file:

```json
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
```
> It appears that new versions of Storybook have an issue with react and react-dom as peer dependencies. The current suggestions I found was to either put these dependencies in the dependencies section of the package.json or downgrade the version of Storybook. Let's admit it, both suck. I tried something and it seems to be working - in the components package react and react-dom are still peer dependencies, but on the monorepo's root project I have them as dependencies. Yeah, I think that this is a less horrible solution...    

I guess it is time to see if anything works, right?

```bash
Yarn storybook
```

**Yep!** We’re up and running with some out-of-the-box stories.
It is time to write my own story for the Pagination component just to make sure it all works as expected:

```javascript
import React from 'react';
import Pagination from '../Pagination';
 
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
 title: 'Example/Pagination',
 component: Pagination,
 // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
 argTypes: {
   onPageChange:{ action: 'Page changed' },
 },
};
 
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template = (args) => <div><Pagination {...args} /></div>;
 
export const Simple = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Simple.args = {
   pagesCount:10,
   cursor:3,
   pagesBuffer:5,
};
```

And here is my component:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u1cba440q3idv2580zd8.png) 

Nothing too fancy with some functionality issues, but this is a good start :) I have a component on my monorepo which gets included in my component catalog (a.k.a Storybook).

Before we go and commit these changes, let’s set our ESlint configuration for this package to suit the React nature of it. I will add the `eslint-plugin-react` plugin to the package’s dev dependencies

```bash
yarn add eslint-plugin-react -D
```

And my .eslintrc.json looks like this:

```json
{
   "env": {
       "browser": true,
       "commonjs": true
   },
   "extends": ["eslint:recommended", "plugin:react/recommended"],
   "parserOptions": {
       "ecmaVersion": 2020,
       "sourceType": "module",
       "ecmaFeatures": {
           "jsx": true
       }
   },
   "rules": {}
}
```

(sourceType as modules to support ESM import/export and ecmaFeatures including “jsx” for React)

So if I push this components package to my GitHub repo, will it get published as part of my Monorepo’s GitHub action? Let’s check - 

I add all the changes and give my commit message:

```bash
feat: Add the first component to the components package
```

GutHub action starts the pipeline, and sure enough it got published to NPM:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u9be6x73jw67eoj3yx21.png) 

Nice :)

Wait… I’m noticing that although I did not change the other package I have on the monorepo (eslint-plugin-craftsmanslint) it still got version bumped and published to NPM. This is a _disturbing_ surprise. Why is this happening?
From this [thread](https://stackoverflow.com/questions/60180630/lerna-always-lists-all-packages-ready-to-publish-when-running-workflow-of-github) I’m learning that it might be to do with the fact that when the GitHub action checks out the code it does not fetch the entire tags history with it, and therefore Lerna has a hard time determining what really changed. So I’m adding to the checkout command on the npm-publish.yml that instruction.

```yml
- uses: actions/checkout@v2
      with:
        fetch-depth: 0
```

Let’s try now to publish some fixes to the components package and see if only the components package gets updated and published. I added an empty test just for the sake of it, and pushed it…

Yes! Only the components package was published.

So there we have it:
A new components package under the Pedalboard monorepo with a single (and quite ugly I admit)  component in it. We also have a cool Storybook catalog which can display this component and any future one to come. We have Eslint working and even an empty test ;) we made sure that only the packages that were changed get published to npm by fetching all the tags history when checking out the code on our GitHub action and… I think that this is it.

Of course, all recent code resides on the [Pedalboard monorepo](https://github.com/mbarzeev/pedalboard) so you can check the recent code changes there, and as always if you have any ideas on how to make this better or any other technique, be sure to share with the rest of us!

*Hey! If you liked what you've just read check out <a href="https://twitter.com/mattibarzeev?ref_src=twsrc%5Etfw" class="twitter-follow-button" data-show-count="false">@mattibarzeev</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> on Twitter* :beers:

<small><small><small>Photo by <a href="https://unsplash.com/@nahakiole?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Robin Glauser</a> on <a href="https://unsplash.com/s/photos/components?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></small></small></small>