How to create react monorepo with npm workspace, webpack, and create-react-app
Tags: monorepo, workspace, react, webpack
Published: 2021-10-10 16:39:09

> *Workspaces is a generic term that refers to the set of features in the npm cli that provides support to managing multiple packages from your local files system from within a singular top-level, root package.* [*(npm workspaces)*](https://docs.npmjs.com/cli/v7/using-npm/workspaces)

```
.
├── package.json
│
└── web
    │
    ├── components
    │   └── package.json
    │
    └── modules
        │
        ├── home
        │   └── package.json
        │
        └── root
            └── package.json
```
*repository: [https://github.com/ynwd/monorepo/tree/init](https://github.com/ynwd/monorepo/tree/init)*

Create and enter app directory
```
mkdir mono && cd mono
```

Create components and modules dir
```
mkdir -p web/components web/modules
```

## Create shared components
```
cd web/components
```

init a package
```
npm init -y
```

install webpack
```
npm i webpack webpack-cli -D
```

install react
```
npm i react react-dom
```

install babel and webpack loader
```
npm i @babel/core @babel/preset-env @babel/preset-react babel-loader -D
```

create a first component
```js
// src/index.js
import React from "react"

const Header = ({ text = "" }) => {
    return (
        <h1>Shared header library {text}</h1>
    )
}

export {
    Header
}
```

create webpack config
```js
// webpack.config.js
const path = require("path")

module.exports = {
    mode: "production",
    entry: {
        index: { import: "./src/index.js" }
    },
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                loader: "babel-loader",
            },
        ],
    },
    output: {
        filename: "components.bundle.min.js",
        library: 'fstrComponents',
        libraryTarget: 'umd',
        clean: true
    },
}
```

create babel config
```js
// babel.config.js
module.exports = {
    presets: [
        [
            "@babel/preset-react",
            {
                targets: {
                    node: "current",
                },
            },
        ],
    ],
};
```

update package name, module, and script:
```json
// package.json
{
  "name": "@fstr/components",
  "version": "1.0.0",
  "description": "",
  "module": "dist/components.bundle.min.js",
  "scripts": {
    "build": "webpack",
    "build-watch": "webpack --watch",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.15.8",
    "@babel/preset-env": "^7.15.8",
    "@babel/preset-react": "^7.14.5",
    "babel-loader": "^8.2.2",
    "webpack": "^5.58.1",
    "webpack-cli": "^4.9.0"
  },
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  }
}
```

build
```
npm run build
```

## Create react app

back to app root dir
```
cd ../../ 
```
create root module
```
npx create-react-app web/modules/root
```

create home module
```
npx create-react-app web/modules/home
```

update all packages name
```json
{
  "name": "@fstr/home"
}
```
```json
{
  "name": "@fstr/root"
}
```

## Link all modules and components together
still in the app root folder, create npm workspace
```json
// package.json
{
    "name": "@fstr/monorepo",
    "private": true,
    "version": "1.0.0",
    "workspaces": [
        "./web/*",
        "./web/modules/*"
    ]
}
```

Link all modules and components together. 
```
npm install
```
This will create symlink on node_modules. 

Now you can run all modules from app root dir.
```
npm run build -w @fstr/components
```

Run root module
```
npm start -w @fstr/root
```

Run home module
```
npm start -w @fstr/home
```

## Import shared component from modules
Now you can import shared component from all modules
```jsx
// web/modules/root/src/App.js
import logo from './logo.svg'
import './App.css'
import { Header } from "@fstr/components"

function App() {
  return (
    <div className="App">
      <Header text="from root" />
    </div>
  )
}

export default App

```

Run the root module to see the changes
```
npm start -w @fstr/root
```