Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

Building Scalable Front-end With Lerna, YARN And React In 60 Minutes

Beginnings are often messy. Be it in any IT project, we often see that at a certain point, people look forward to revamping things. With revamping, there comes additional costs and additional time. And those will be a lot costlier if not addressed at the right time to meet the customers’ feature demands. Most things like code organization, reusability, code cleanup, documentation are often left unattended at the beginning only to realize that they hold the key to faster development and ensure quick delivery of requested features in the future, as projects grow into platforms to serve huge numbers of users.

We are going to look at how to write a scalable frontend platform with React and Lerna within an hour in this blog post. The goal is to have an organized modular architecture that makes it easy to maintain existing products and can quickly deliver new modules as they arrive.

Going Mono-Repo:

Most projects start with a single git/bitbucket repository and end up in chaos with time. With mono-repo, we can make it more manageable. That being said, we will use Lerna, npm and YARN to initialize our monorepo.

Prerequisite:

npm, npx

Installing YARN: Installation Guide

Installing npx

CODE: https://gist.github.com/velotiotech/d5dd13af5392cb77da8f25b823e0d783.js

Installing Lerna

CODE: https://gist.github.com/velotiotech/7071845ac40fb1fa57f5f8ba0dd48476.js

After this, we will have Lerna installed globally.

Initializing a project with Lerna.

CODE: https://gist.github.com/velotiotech/88d63cc912fccbf81af1a3ae5872d164.js

Lerna init output

Let's go through the files generated with it. So we have the following files:

 - package.json 

 - lerna.json

 - packages/

package.json is the same as any other npm package.json file. It specifies the name of the project and some basic stuff that we normally define like adding husky for pre-commit hooks.

lerna.json is a configuration file for configuring Lerna. You can find more about Lerna configuration and supported options at Lerna concepts.

packages/ is a directory where all our modules will be defined and Lerna will take care of referencing them in each other. Lerna will simply create symlinks of referenced local modules to make it available for other modules to use.

To understand Lerna and its concepts, you can go through its official documentation.

For better performance, we will go with YARN. So how do we configure YARN with Lerna?

It's pretty simple as shown below.

Default package.json from lerna.init

We just need to add “npmClient”: “yarn” to lerna.json

Using YARN workspaces with Lerna

YARN workspace is a quick way to get around the mess of `yarn link` i.e. referencing one module into another. Lerna already provides it so why go with YARN workspaces? 

The answer is excellent bootstrapping time provided by YARN workspaces. 

Here is how we can use YARN workspaces with Lerna

CODE: https://gist.github.com/velotiotech/9392bfcee47a9539e4210f9ef4cad126.js

Now we just need to add “useWorkspaces”: true to lerna.json and in package.json, we need to add 

CODE: https://gist.github.com/velotiotech/58d9e73ba1bef36134f91a848e69d917.js

CODE: https://gist.github.com/velotiotech/8f96501b483bda82365020a9c543c0dc.js

This will take care of linking different modules in a UI platform which are mentioned in the packages folder.

Once this is done, we can proceed to bootstrap, which in other terms, means forcefully telling Lerna to link the packages. As of now, we do not have anything under it, but we can run it to check if this setup can bootstrap and work properly.

CODE: https://gist.github.com/velotiotech/20956913a03b401d6f89fbe8ab9997ef.js

Reinstalling dependencies and bootstrapping output


So it's all about the Lerna and YARN set up, but how should one really organize a UI package to build in a manageable and modular way.

What most React projects are made of

1 - Component Libraries
2 - Modules
3 - Utils libraries
4 - Abstractions over React-Redux ( Optional but most organizations go with it)

Components: Most organizations end up building their own component libraries and it is crucial to have it separated from the codebase for reusability of components. There exist a lot of libraries, but when you start building the product, you realize every library has something and misses something. Most commonly available libraries convey a standard UX design pattern. What if your designs don’t fit into those at a later point? So we need a separate components library where we can maintain organization-specific components.

Modules: At first you may have one module or product, but over time it will grow. To avoid breaking existing modules over time and keeping change lists smaller and limited to individual products, it's essential to split a monolith into multiple modules to manage the chaos of each module within it without impacting other stable modules.

Utils: These are common to any project. Almost every one of us ends up creating utils folders in projects to help with little functions like converting currency or converting large numbers like 100000 as 100K and many more. Most of these functions are common and specific to organizations. E.g. a company working with statistics is going to have a helper function to convert large numbers into human-readable figures and eventually they end up copying the same code. Keeping utils separated gives us a unique opportunity to avoid code duplication of such cases and keep consistency across different modules.

Abstractions over React-Redux: A lot of organizations prefer to do it. AWS, Microsoft Outlook, and many more have already adopted this strategy to abstract React & Redux bindings to create their own simplified functions to quickly bootstrap a new module/product into an existing ecosystem. This helps in faster delivery of new modules since developers don’t get into the same problems of bootstrapping and can focus on product problems rather than setting up the environment. 

One of the most simplified approaches is presented at react-redux-patch to reduce boilerplate. We will not go into depth in this article since it's a vast topic and a lot of people have their opinion on how this should be built. 

Example:

We will use create-react-app & create-react-library to create a base for our libraries and modules.

Installing create-react-library globally.

CODE: https://gist.github.com/velotiotech/2e37a209f75101cbd1679947ffcf4f6d.js

Installing create-react-library globally output

Creating a components library:

create-react-library takes away the pain of complex configurations and enables us to create components and utility libraries with ease.

CODE: https://gist.github.com/velotiotech/daf152046e247c1b6e68b20cd552da60.js

Creating UI-Components lib with create-react-library output


For starters, just create a simple button component in the library.

CODE: https://gist.github.com/velotiotech/5d8261f85aead37a5ff49e4c0ab837d6.js

Similarly, we can create common-utils packages with the help of create-react-library.

Common Utils package generation using create-react-library output


We will just define a simple formatDate function in the common-utils library.

CODE: https://gist.github.com/velotiotech/bf8508c9de1121e5aa66064945884799.js

Now these two packages, ui-components and common-utils are ready to be used in multiple projects.

Let’s create a simple React app with create-react-app.

CODE:https://gist.github.com/velotiotech/41d8827c541c3e2ad66cf652637ff4d2 .js

Create-react-app output for the target product


To link these libraries, simply run 

CODE: https://gist.github.com/velotiotech/238f9c851927b944feda52c1cfa806c5.js

whenever you add a new library to package.json

And we will be ready to use them.

Let’s add the dependencies in package.json

CODE: https://gist.github.com/velotiotech/bb53acccb77520def0577762a1a19752.js

Let's do Lerna bootstrap:

Bootstrapping output to link components

Here is a simple example of how we can use it.

CODE: https://gist.github.com/velotiotech/1c6f2e470b2402c1b375c6127e4ae65a.js

Real manageable chaos:

With this strategy, our codebase is now split, can be easily reused, and is modular.

- Every module is placed under packages. 

- Product codebase is separated from utils and components

- Multiple teams can work on individual modules

- Finding impact analysis is easier than a monolith project

- Side effects are mostly limited to only one module for small changes

Final Project structure

All the code in this article is available at lerna-yarn-react-example

How about publishing the packages?

Publishing your packages to a private npm directory is a chaotic thing in monorepo. Lerna provides a very simple approach towards publishing packages.

You will just need to add the following to package.json

CODE: https://gist.github.com/velotiotech/76d862cc322dbb172bfa60bab8581f71.js

CODE: https://gist.github.com/velotiotech/82be550ac3293e300ef8ce19ca1f0707.js

Now you can simply run

CODE: https://gist.github.com/velotiotech/20b04e716202c0a07ca6b6960c2dc14e.js

It will try to publish the packages that have been changed since the last commit.

Summary:

With the use of Lerna and YARN, we can create an efficient front-end architecture to quickly deliver new features with less impact on existing modules. Of course with additional bootstrapping tools like yeoman generator along with abstractions over React and Redux, it makes the process of introducing to new modules a piece of a cake. Over time, you can easily split these modules and components into individual repositories by utilizing the private npm repositories. But for the initial chaos of getting things working and quick prototyping of your next big company’s UI architecture, Lerna and YARN are perfectly suited tools!!!