Setting up React Typescript Monorepo with Lerna

While working on complicated software solutions, many times we need to create multiple projects which may have some common building blocks, or some other sharing code.

Monorepos are basically the architectural pattern which sets all these multiple projects together. All the dependencies are stored in one single big repo.And makes it easy to share the code. You can know more about monorepos here.

As the title says, we are going to setup a react typescript monorepo by using the tool Lerna.

Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.

Also first things first, if you like my content and would love to know more about my future posts. Please follow me from the sidebar on this page. Also you can join the newsletter.

Goal

We will create two packages.

One as Application (react app container) and other as ComponentLibrary (where we can store some common components).

Our goal is to successfully set up both repo. And use code from ComponentLibrary into the Application.

If you want to skip and just download the repository, click here.

Lets start with setting up the project.

Create the base repository and setup Lerna

Let’s start by creating a folder by the name lerna-monorepo. And install lerna as a global package

npm i lerna -g
cd lerna-monorepo

Initialize the project as a monorepo by lerna inside the lerna-monorepo folder

lerna init

You must be able to see some folder structure like this.

Folder structure after initialize lerna

Here we have the package.json for obvious reasons. And we also have the lerna.json to configure lerna in it.

Inside packages we can keep the dependent modules.

Setup the react typescript webapp

For setting up the react repo, we are going to set it up from scratch. I am going to follow the official typescript react setup docs. The link is here. You can reference it for more details.

We are going to install it inside the packages folder. Let’s create a folder named as Application.

And make two folders inside, src & dist.

TypeScript files will start out in your src folder, run through the TypeScript compiler, then webpack, and end up in a main.js file in dist.

cd packages
mkdir Application
cd Application
mkdir src dist

Initialize project

Let’s make this repo in a npm repo. This will also add package.json to it

npm init --y

Folder structure must be looking like this now.

Install dependencies

Webpack will be useful to convert the .ts file into a single big .js file.

That @types/ prefix means that we also want to get the declaration files for React and React-DOM. 

Let’s run below commands to install these.

npm i react react-dom --save
npm i @types/react @types/react-dom webpack webpack-cli --save-dev

Now let’s install typescript and other dependencies which will help us to convert the code from .ts to .js.

npm i typescript ts-loader source-map-loader --save-dev

Add Typescript config file

Create a file called as tsconfig.json in the Application folder and paste the following content inside.

{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "module": "commonjs",
        "target": "es6",
        "jsx": "react"
    }
}

Add index.tsx file

Create index.tsx file in the Application/srcfolder

import * as React from "react";
import * as ReactDOM from "react-dom";

export interface HelloProps { name: string; }


class Hello extends React.Component<HelloProps, {}> {
    render() {
        return <h1>Hello from {this.props.name}!</h1>;
    }
}

ReactDOM.render(
    <Hello name="Application" />,
    document.getElementById("example")
);

Add index.html

Create index.html file in the Application folder

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>
    </head>
    <body>
        <div id="example"></div>
    </body>
</html>

Create webpack configuration file

Install html-webpack-plugin, so that the index.html file can have the js inserted by itself.

npm i html-webpack-plugin --save-dev

Add file webpack.config.js to the src/Application folder.

const path = require("path");
const HWP = require("html-webpack-plugin");

module.exports = {
    mode: "production",

    // Enable sourcemaps for debugging webpack's output.
    devtool: "source-map",

    resolve: {
        extensions: [".ts", ".tsx", '.js'],
    },

    module: {
        rules: [
            {
                test: /\.ts(x?)$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: "ts-loader"
                    }
                ]
            },
            // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
            {
                enforce: "pre",
                test: /\.js$/,
                loader: "source-map-loader"
            }
        ]
    },
    plugins: [
        new HWP({
          template: path.resolve(__dirname, "index.html"),
          filename: "index.html",
          inject: "body"
        })
      ]
};

Now webpack is all setup. If you run the below command from folder pakcages/Application , You will be able to run index.html in the browser and will be seeing “Hello from Application”

npx webpack

Obviously, we won’t be running npx webpack after every single line of code change. We want the page to refresh as the code is changed. For that we will use webpack-dev-server.

Let’s install it

npm i webpack-dev-server --save-dev

Also lets make an entry of webpack-dev-server command into the package.json. Add this to the package.json.

"scripts": {
    "start": "webpack-dev-server"
  },

If we run npm run start now, we will be able to access the localhost link where we can view the same result.

Setup the shared folder

So far so good, we have created the monorepo setup, we have installed the react typescript app. Now we need to create another package, which we can use inside of our Application.

Let’s call it ComponentLibrary , where we can keep all our common components to reuse in different applications

cd packages
mkdir ComponentLibrary

Folder structure looks something like this now.

Let’s initialize npm inside ComponentLibrary

npm init --y

Installing Dependencies in ComponentLibrary

We are going to need react and @types/react packages for now.

npm i react --save
npm i @types/react --save-dev

Create index.tsx file in the ComponentLibrary folder and put the following code inside.

import * as React from 'react';


const ComponentLibrary = () => {
    return (
        <h1>Hello from ComponentLibrary!</h1>
    )
}

export default ComponentLibrary;

Connect everthing

We made the Application and the ComponentLibrary packages.

Now it’s time to connect these together.

Let’s add ComponentLibrary as npm package into Application. And also whenever something is changed in ComponentLibarry, the application should update.

Add following code in Application/package.json

"dependencies": {
    "html-webpack-plugin": "^4.3.0",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "ComponentLibrary": "1.0.0"
..

We have added it as dependency, but npm would not be able to find ComponentLibrary anywhere.

Now to actually get this package from the neighbour, we need to run one more tiny step.

Go to the root directory i.e lerna-monorepo and run

lerna bootstrap

This command will link the dependencies together in the repo.

Let’s add the ComponentLibrary into Application/src/index.tsx. Edit the file as below.

...
class Hello extends React.Component<HelloProps, {}> {
    render(): JSX.Element {
        return (<>
        <h1>Hello from {this.props.name}!</h1>
        <ComponentLibrary />
        </>);
    }
}

...

Now if we go to packages/Application and run

npm run start

We are going to see something like below.

Extra Tip: You can also call run the project from base directory i.e. lerna-monorepo, you just have to add the below script in the root level package.json

{
  "name": "root",
  "private": true,
  "devDependencies": {
    "lerna": "^3.22.1"
  },
  "scripts": {
    "start": "npm start --prefix packages/Application"
  }
}

Using this boilerplate code, you can expand your monorepo furthur.

Thanks for reading!

3 thoughts on “Setting up React Typescript Monorepo with Lerna”

  1. Application can’t find Component Library.
    I get this error: TS2304: Cannot find name ‘ComponentLibrary’.

  2. sorry, may bad. forgot to import it on the Application index.tsx.
    my bee you can add the import into Application/src/index.tsx code example.
    great tutorial by the way.
    thanks, and you can remove my comments.

Leave a Reply

Your email address will not be published. Required fields are marked *