Sharing Code in a monorepo
Monorepos let you share code across applications without friction. To do that, you'll be building packages to share code between your apps.
What is a package?
The word 'package' has a double meaning when it comes to monorepos. It can refer to either of these:
- A set of files you download from a registry into your
node_modules
, via a package manager likenpm
. - A workspace containing code that can be shared between applications - by convention usually inside
/packages
.
This dual meaning can be very confusing to folks new to the monorepo scene. You're likely very familiar with package installation, but not so familiar with workspaces.
The fact is that they're very similar. A package is just a piece of shared code. Except that installed packages live in your node_modules
, and local packages lives in a workspace - likely in your /packages
folder.
Anatomy of a package
Each package contains a package.json
. You're likely familiar with using these to manage dependencies and scripts in your applications.
However, you may not have noticed the main
and name
fields before:
{
// The name of your package
"name": "my-lib",
// When this package is used, this file is actually
// the thing that gets imported
"main": "./index.js"
}
Both of these fields are important for deciding how this package behaves when it's imported. For instance, if index.js
has some exports:
export const myFunc = () => {
console.log("Hello!");
};
And we import this file into one of our apps:
import { myFunc } from "my-lib";
myFunc(); // Hello!
Then we'll be able to use the code inside the my-lib
folder inside our applications.
To summarize, each package must have a name
and a main
declared inside its package.json
.
Package resolution in package.json
is a very complicated topic, and we can't do justice to it here. Other fields in your package.json
may take precedence over main
depending on how the package is being imported.
Check the npm docs (opens in a new tab) for a guide.
For our purposes, using main
will be good enough.
Next steps
We're going to introduce two styles of packages - internal packages and external packages:
Internal packages are intended to only be used inside the monorepo where they're housed. They are relatively simple to set up, and if your project is closed source they will be the most useful to you.
External packages are bundled and sent to a package registry. This is useful for design systems, shared utility libraries or any open source work. However, they introduce more complexity around bundling, versioning and publishing.