• United States+1
  • United Kingdom+44
  • Afghanistan (‫افغانستان‬‎)+93
  • Albania (Shqipëri)+355
  • Algeria (‫الجزائر‬‎)+213
  • American Samoa+1684
  • Andorra+376
  • Angola+244
  • Anguilla+1264
  • Antigua and Barbuda+1268
  • Argentina+54
  • Armenia (Հայաստան)+374
  • Aruba+297
  • Australia+61
  • Austria (Österreich)+43
  • Azerbaijan (Azərbaycan)+994
  • Bahamas+1242
  • Bahrain (‫البحرين‬‎)+973
  • Bangladesh (বাংলাদেশ)+880
  • Barbados+1246
  • Belarus (Беларусь)+375
  • Belgium (België)+32
  • Belize+501
  • Benin (Bénin)+229
  • Bermuda+1441
  • Bhutan (འབྲུག)+975
  • Bolivia+591
  • Bosnia and Herzegovina (Босна и Херцеговина)+387
  • Botswana+267
  • Brazil (Brasil)+55
  • British Indian Ocean Territory+246
  • British Virgin Islands+1284
  • Brunei+673
  • Bulgaria (България)+359
  • Burkina Faso+226
  • Burundi (Uburundi)+257
  • Cambodia (កម្ពុជា)+855
  • Cameroon (Cameroun)+237
  • Canada+1
  • Cape Verde (Kabu Verdi)+238
  • Caribbean Netherlands+599
  • Cayman Islands+1345
  • Central African Republic (République centrafricaine)+236
  • Chad (Tchad)+235
  • Chile+56
  • China (中国)+86
  • Christmas Island+61
  • Cocos (Keeling) Islands+61
  • Colombia+57
  • Comoros (‫جزر القمر‬‎)+269
  • Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)+243
  • Congo (Republic) (Congo-Brazzaville)+242
  • Cook Islands+682
  • Costa Rica+506
  • Côte d’Ivoire+225
  • Croatia (Hrvatska)+385
  • Cuba+53
  • Curaçao+599
  • Cyprus (Κύπρος)+357
  • Czech Republic (Česká republika)+420
  • Denmark (Danmark)+45
  • Djibouti+253
  • Dominica+1767
  • Dominican Republic (República Dominicana)+1
  • Ecuador+593
  • Egypt (‫مصر‬‎)+20
  • El Salvador+503
  • Equatorial Guinea (Guinea Ecuatorial)+240
  • Eritrea+291
  • Estonia (Eesti)+372
  • Ethiopia+251
  • Falkland Islands (Islas Malvinas)+500
  • Faroe Islands (Føroyar)+298
  • Fiji+679
  • Finland (Suomi)+358
  • France+33
  • French Guiana (Guyane française)+594
  • French Polynesia (Polynésie française)+689
  • Gabon+241
  • Gambia+220
  • Georgia (საქართველო)+995
  • Germany (Deutschland)+49
  • Ghana (Gaana)+233
  • Gibraltar+350
  • Greece (Ελλάδα)+30
  • Greenland (Kalaallit Nunaat)+299
  • Grenada+1473
  • Guadeloupe+590
  • Guam+1671
  • Guatemala+502
  • Guernsey+44
  • Guinea (Guinée)+224
  • Guinea-Bissau (Guiné Bissau)+245
  • Guyana+592
  • Haiti+509
  • Honduras+504
  • Hong Kong (香港)+852
  • Hungary (Magyarország)+36
  • Iceland (Ísland)+354
  • India (भारत)+91
  • Indonesia+62
  • Iran (‫ایران‬‎)+98
  • Iraq (‫العراق‬‎)+964
  • Ireland+353
  • Isle of Man+44
  • Israel (‫ישראל‬‎)+972
  • Italy (Italia)+39
  • Jamaica+1876
  • Japan (日本)+81
  • Jersey+44
  • Jordan (‫الأردن‬‎)+962
  • Kazakhstan (Казахстан)+7
  • Kenya+254
  • Kiribati+686
  • Kosovo+383
  • Kuwait (‫الكويت‬‎)+965
  • Kyrgyzstan (Кыргызстан)+996
  • Laos (ລາວ)+856
  • Latvia (Latvija)+371
  • Lebanon (‫لبنان‬‎)+961
  • Lesotho+266
  • Liberia+231
  • Libya (‫ليبيا‬‎)+218
  • Liechtenstein+423
  • Lithuania (Lietuva)+370
  • Luxembourg+352
  • Macau (澳門)+853
  • Macedonia (FYROM) (Македонија)+389
  • Madagascar (Madagasikara)+261
  • Malawi+265
  • Malaysia+60
  • Maldives+960
  • Mali+223
  • Malta+356
  • Marshall Islands+692
  • Martinique+596
  • Mauritania (‫موريتانيا‬‎)+222
  • Mauritius (Moris)+230
  • Mayotte+262
  • Mexico (México)+52
  • Micronesia+691
  • Moldova (Republica Moldova)+373
  • Monaco+377
  • Mongolia (Монгол)+976
  • Montenegro (Crna Gora)+382
  • Montserrat+1664
  • Morocco (‫المغرب‬‎)+212
  • Mozambique (Moçambique)+258
  • Myanmar (Burma) (မြန်မာ)+95
  • Namibia (Namibië)+264
  • Nauru+674
  • Nepal (नेपाल)+977
  • Netherlands (Nederland)+31
  • New Caledonia (Nouvelle-Calédonie)+687
  • New Zealand+64
  • Nicaragua+505
  • Niger (Nijar)+227
  • Nigeria+234
  • Niue+683
  • Norfolk Island+672
  • North Korea (조선 민주주의 인민 공화국)+850
  • Northern Mariana Islands+1670
  • Norway (Norge)+47
  • Oman (‫عُمان‬‎)+968
  • Pakistan (‫پاکستان‬‎)+92
  • Palau+680
  • Palestine (‫فلسطين‬‎)+970
  • Panama (Panamá)+507
  • Papua New Guinea+675
  • Paraguay+595
  • Peru (Perú)+51
  • Philippines+63
  • Poland (Polska)+48
  • Portugal+351
  • Puerto Rico+1
  • Qatar (‫قطر‬‎)+974
  • Réunion (La Réunion)+262
  • Romania (România)+40
  • Russia (Россия)+7
  • Rwanda+250
  • Saint Barthélemy (Saint-Barthélemy)+590
  • Saint Helena+290
  • Saint Kitts and Nevis+1869
  • Saint Lucia+1758
  • Saint Martin (Saint-Martin (partie française))+590
  • Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)+508
  • Saint Vincent and the Grenadines+1784
  • Samoa+685
  • San Marino+378
  • São Tomé and Príncipe (São Tomé e Príncipe)+239
  • Saudi Arabia (‫المملكة العربية السعودية‬‎)+966
  • Senegal (Sénégal)+221
  • Serbia (Србија)+381
  • Seychelles+248
  • Sierra Leone+232
  • Singapore+65
  • Sint Maarten+1721
  • Slovakia (Slovensko)+421
  • Slovenia (Slovenija)+386
  • Solomon Islands+677
  • Somalia (Soomaaliya)+252
  • South Africa+27
  • South Korea (대한민국)+82
  • South Sudan (‫جنوب السودان‬‎)+211
  • Spain (España)+34
  • Sri Lanka (ශ්‍රී ලංකාව)+94
  • Sudan (‫السودان‬‎)+249
  • Suriname+597
  • Svalbard and Jan Mayen+47
  • Swaziland+268
  • Sweden (Sverige)+46
  • Switzerland (Schweiz)+41
  • Syria (‫سوريا‬‎)+963
  • Taiwan (台灣)+886
  • Tajikistan+992
  • Tanzania+255
  • Thailand (ไทย)+66
  • Timor-Leste+670
  • Togo+228
  • Tokelau+690
  • Tonga+676
  • Trinidad and Tobago+1868
  • Tunisia (‫تونس‬‎)+216
  • Turkey (Türkiye)+90
  • Turkmenistan+993
  • Turks and Caicos Islands+1649
  • Tuvalu+688
  • U.S. Virgin Islands+1340
  • Uganda+256
  • Ukraine (Україна)+380
  • United Arab Emirates (‫الإمارات العربية المتحدة‬‎)+971
  • United Kingdom+44
  • United States+1
  • Uruguay+598
  • Uzbekistan (Oʻzbekiston)+998
  • Vanuatu+678
  • Vatican City (Città del Vaticano)+39
  • Venezuela+58
  • Vietnam (Việt Nam)+84
  • Wallis and Futuna+681
  • Western Sahara (‫الصحراء الغربية‬‎)+212
  • Yemen (‫اليمن‬‎)+967
  • Zambia+260
  • Zimbabwe+263
  • Åland Islands+358
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

Saurabh Nemade

Full-stack Development

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

npm install -g npx
view raw install_npm.js hosted with ❤ by GitHub

Installing Lerna

npm install --global lerna
view raw lerna.js hosted with ❤ by GitHub

After this, we will have Lerna installed globally.

Initializing a project with Lerna.

mkdir startup_ui && cd startup_ui
npx lerna init

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

{
"name": "root",
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"lerna": "^3.20.2"
}
}
view raw package.json hosted with ❤ by GitHub

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

"workspaces": [
"packages/*"
]
view raw workspace.js hosted with ❤ by GitHub

{
"packages": [
"packages/*"
],
"version": "0.0.0",
"npmClient": "yarn",
"useWorkspaces": true
}
view raw lerna.json hosted with ❤ by GitHub

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.

yarn install
lerna bootstrap
view raw install_yarn.js hosted with ❤ by GitHub

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.

yarn global add create-react-library

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.

cd packages
create-react-library ui-components
view raw component.js hosted with ❤ by GitHub

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


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

import React from 'react'
const buttonStyle = {
padding: '10px 20px'
};
const Button = (props) => {
return (
<button
className="btn btn-default"
style={buttonStyle}
onClick={props.handleClick}>{props.label}</button>
)
}
Button.defaultProps = {
onClick: () => {},
label: ''
};
export default Button;

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.

export const formatDate = (date) => {
var d = new Date(date),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
if (month.length < 2)
month = '0' + month;
if (day.length < 2)
day = '0' + day;
return [year, month, day].join('-');
}
view raw common-utils hosted with ❤ by GitHub

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 

lerna bootstrap
view raw lerna.js hosted with ❤ by GitHub

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

{
"name": "product-one",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"ui-components": "latest",
"common-utils": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Let's do Lerna bootstrap:

Bootstrapping output to link components

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

import React from 'react';
import logo from './logo.svg';
import './App.css';
import { Button } from 'ui-components';
import { formatDate } from 'common-utils';
function App() {
return (
<div className="App">
<div>
Sample product
<Button label="A product button" />
<span>
Today is: {formatDate(new Date())}
</span>
</div>
</div>
);
}
export default App;
view raw gistfile1.txt hosted with ❤ by GitHub

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

"publishConfig": {
"registry": REGISTRY_URL_HERE
}

"publishConfig": {
"registry": REGISTRY_URL_HERE
}

Now you can simply run

lerna publish
view raw publish.js hosted with ❤ by GitHub

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!!!

Get the latest engineering blogs delivered straight to your inbox.
No spam. Only expert insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings

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

npm install -g npx
view raw install_npm.js hosted with ❤ by GitHub

Installing Lerna

npm install --global lerna
view raw lerna.js hosted with ❤ by GitHub

After this, we will have Lerna installed globally.

Initializing a project with Lerna.

mkdir startup_ui && cd startup_ui
npx lerna init

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

{
"name": "root",
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"lerna": "^3.20.2"
}
}
view raw package.json hosted with ❤ by GitHub

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

"workspaces": [
"packages/*"
]
view raw workspace.js hosted with ❤ by GitHub

{
"packages": [
"packages/*"
],
"version": "0.0.0",
"npmClient": "yarn",
"useWorkspaces": true
}
view raw lerna.json hosted with ❤ by GitHub

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.

yarn install
lerna bootstrap
view raw install_yarn.js hosted with ❤ by GitHub

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.

yarn global add create-react-library

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.

cd packages
create-react-library ui-components
view raw component.js hosted with ❤ by GitHub

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


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

import React from 'react'
const buttonStyle = {
padding: '10px 20px'
};
const Button = (props) => {
return (
<button
className="btn btn-default"
style={buttonStyle}
onClick={props.handleClick}>{props.label}</button>
)
}
Button.defaultProps = {
onClick: () => {},
label: ''
};
export default Button;

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.

export const formatDate = (date) => {
var d = new Date(date),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
if (month.length < 2)
month = '0' + month;
if (day.length < 2)
day = '0' + day;
return [year, month, day].join('-');
}
view raw common-utils hosted with ❤ by GitHub

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 

lerna bootstrap
view raw lerna.js hosted with ❤ by GitHub

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

{
"name": "product-one",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"ui-components": "latest",
"common-utils": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Let's do Lerna bootstrap:

Bootstrapping output to link components

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

import React from 'react';
import logo from './logo.svg';
import './App.css';
import { Button } from 'ui-components';
import { formatDate } from 'common-utils';
function App() {
return (
<div className="App">
<div>
Sample product
<Button label="A product button" />
<span>
Today is: {formatDate(new Date())}
</span>
</div>
</div>
);
}
export default App;
view raw gistfile1.txt hosted with ❤ by GitHub

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

"publishConfig": {
"registry": REGISTRY_URL_HERE
}

"publishConfig": {
"registry": REGISTRY_URL_HERE
}

Now you can simply run

lerna publish
view raw publish.js hosted with ❤ by GitHub

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!!!

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings