We're working on something new! Hook Relay gives you Stripe-quality webhooks in minutes. Sign up for free today!

Monorepo Javascript Projects with Yarn Workspaces and Lerna

Typically, every project that you do will have its own git repository. This approach can be cumbersome when those projects are closely tied. Monorepos promise a more convenient alternative. In this article, Saiharsha Balasubramaniam shows how to set up and manage a monorepo for a JS project using Yarn Workspaces and Lerna.

Monorepo is a software development strategy in which a single repository contains code for multiple projects with shared dependencies. It has a number of advantages:

  • It is easier to manage dependencies in monorepo projects. Common dependency versions are used, which saves a lot of time and computational power.
  • It is easier to refactor code across all packages.
  • Reusability of code is ensured.

And, like everything else in the world, the monorepo approach has certain disadvantages:

  • Including multiple versions of a dependency in different packages might cause dependency conflicts.
  • It degrades performance in version control systems, such as Git, due to higher memory usage.
  • Higher chances of merge conflicts.
  • Initial setup takes a long time.

Tools Used to Set Up a Monorepo Project

  • Lerna is used to optimize the management of monorepos. We'll use this tool to manage shared dependencies.
  • Yarn Workspaces is used to optimize and link different packages together.
  • Storybook is used to build and test UI components.

Lerna

Lerna is a tool used to manage monorepos. The repositories are structured into sub repositories. It is typically used in large codebases for shared dependency management and code deployment. Lerna has two major features, namely bootstrap and publish.

lerna bootstrap

This is a command provided by Lerna that does the following:

  • It installs the dependencies of all the packages within the monorepo.
  • It creates links between shared dependencies so that the same package is not installed twice.
lerna publish

The publish command publishes the package updated since the last version release.

Yarn workspaces

Yarn workspaces are used to optimize dependency management. When we use yarn workspaces, all project dependencies are installed in one go. Tools like Lerna make use of Yarn workspaces' low-level primitives.

Using Yarn Workspaces

Let us assume that we have two repositories, namely packages/repo-a and packages/repo-b, within our monorepo structure. To use workspaces, add the following to the package.json file of the root repository.

{
  "private": true,
  "workspaces": ["packages/*"]
}

This adds all the folders within packages as a Yarn workspace. Now, if we run yarn install, dependencies of both repo-a and repo-b are installed.

Setting up Your Project

We will be using yarn as a package manager. To set up Yarn in your machine, install it from the official yarn website.

Let us create a package.json for our project:

{
  "name": "monorepo",
  "version": "1.0.0",
  "private": true,
  "workspaces": ["packages/*"]
}

The workspaces option is used to specify which subfolder contains the various packages in our monorepo. Each folder within packages will be considered a separate project.

Now, let us set up Lerna as a developer dependency of our project. Create a new folder called monorepo. Open a terminal window and enter the following command:

yarn add lerna -D -W # add lerna as developer dependency, in the workspace root
yarn lerna init

This initializes a lerna.json configuration file. This file contains configuration parameters through which we can set up commands for various tasks. We can also define which package manager Lerna uses, such as npm or yarn. The above command also initializes a package folder where the projects can be located. In the lerna.json file, add the npmClient option to specify yarn as the package manager.

{
  "packages": ["packages/*"],
  "npmClient": "yarn",
  "version": "0.0.0",
  "useWorkspaces": true
}

We have successfully set up the boilerplate for our monorepo. Now, let us set up a UI component library and a framework for testing the UI component library.

cd packages
mkdir monorepo-storybook && cd monorepo-storybook
yarn init

When you run yarn init, select all the default options. Let us install the required dependencies.

yarn add react react-dom
yarn add babel-loader -D

You may have noticed that the dependencies were not installed in a node_modules folder in the monorepo-storybook folder. Instead, it was installed within the node_modules folder in the root folder. This is how monorepos work with shared dependencies.

Now, let us configure storybook. Out storybook will be initialized, and the scripts required to install storybook will be configured.

npx sb init

Once it is configured, run the following script to start storybook:

yarn storybook

Some sample stories have been created for us. Let us explore and check out the storybook interface.

Storybook Interface Storybook's Interface

Our storybook setup has been configured successfully. Now, let us create our component library. This will be under a different package. Under the packages folder, create a folder named components and then initialize the package by creating a package.json file.

Note: Storybook isn't directly related to monorepos. It is just a framework for creating UI components. We are using Storybook to demonstrate the use of monorepos.

{
  "name": "components",
  "version": "1.0.0"
}

Create a file named Welcome.js. Let us create a simple React component that displays a name, based on the prop passed to it.

// Importing the react library
import React from "react";

export default function Welcome(props) {
  // Display the name passed as props
  return <h1>Hello, {props.name}</h1>;
}

Let us now initialize a story in storybook. Create a file called Welcome.stories.js within monorepo-storybook/stories folder.

// Importing the react library
import React from "react";
// The storiesOf API is used to display stories in storybook
import { storiesOf } from "@storybook/react";
// Importing our react component
import Welcome from "../../components/Welcome";
// Displaying the component
storiesOf("Welcome", module).add("Welcome component", () => (
  <Welcome name="Harsha"></Welcome>
));

The storiesOf API is used to create and display stories. Let us now check the browser. We can see that a new story is created, and our component is displayed.

Storybook Output The component as viewed in Storybook

Conclusion

Let us recap what we've learned in this article.

  • We learned about monorepo projects and how they are prominent in open-source projects.
  • We discussed the pros and cons of using the monorepo structure in a project.
  • We learned about various tools, such as Yarn Workspaces, Lerna, and Storybook, which we used to set up the monorepo project.
  • We walked through the steps involved in creating the project.
  • We learned how to set up Storybook and created a components library.

You can find the final code for everything we’ve discussed at the following link.

Further Reading

You can expand upon your knowledge by checking out the following resources. Happy learning!

Honeybadger has your back when it counts.

We're the only error tracker that combines exception monitoring, uptime monitoring, and cron monitoring into a single, simple to use platform. Our mission: to tame production and make you a better, more productive developer.

Learn more
author photo

Saiharsha Balasubramaniam

Saiharsha Balasubramaniam is a senior undergraduate, majoring in Computer Science at Amrita Vishwa Vidyapeetham University, India. He is also a passionate software developer and an avid researcher. He designs and develops websites, and loves blockchain technology. Currently, he is an SDE Intern at Flipkart and a Microsoft Learn Student Ambassador.

“We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release.” 
Michael Smith
Try Error Monitoring Free for 15 Days
Are you using Bugsnag, Rollbar, or Airbrake for your monitoring? Honeybadger includes exception, uptime, and check-in monitoring — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
Try Error Monitoring Free for 15 Days