If you're developing a library and an app in a monorepo, the
npm link is your friend. It allows you to use the library the same way you're using a library you're downloading from npm. But it's more flexible: changes to the library immediately show in the app.
If you're in a hurry, skip to the "wrapping it up" section or scan this picture.
Digression: anatomy of an npm package
Technically speaking, an npm package is simply a folder someone uploaded to npm. This folder must contain a
package.json to be helpful. Despite bearing the same name, this file differs slightly from the
package.json you use in app development. In particular, it must define an entry point. Usually, that's the
More modern libraries may use
module instead of
module activates the ESM module system, producing more compact binaries.
That's it. Most npm packages contain many files, but at the heart of it, an npm package is simply a folder consisting of at least two files.
Publishing the library locally
Most frameworks offer a standard way to create and publish npm packages. In the case of Angular, that's using ng-packagr. When writing this article, I quickly checked React and Vue.js. To my surprise, neither framework seems to offer a standard way, but there are enough tutorials to figure it out quickly. So, I'll skip this.
Let's assume you've compiled your library into a folder called
Theoretically, you could upload the library to the global npm repository by running
npm publish from the
dist folder. But if you're like me, you prefer to test it locally, even if it's merely a smoke test. Or maybe you're using a monorepo and don't intend to publish the library. So, let's publish the library locally. It's simple:
Note that the name of the folder becomes the name of the library.
Using the local library
Before compiling the app using the library, you need the counterpart of
npm install my-favorite-library. That's
npm link, plus the name of the library:
Now, the library behaves precisely the way it behaves if installed via
npm install my-favorite-library. Only you've never uploaded it to npm. That's ideal for testing libraries locally and suitable for using private libraries in a corporate network without a custom npm repository.
It's even better than
npm publish && npm install. Each time you build your library,
npm link automatically updates the local
How does it work?
- The local
node_modulesfolder belongs to your application. Every application has its copy of the
node_modulesfolder, each populated with different libraries.
- The global
npm link is another global
node_modules folder usage. After running
npm link without parameters, the global
node_modules folder contains a symbolic link to the current working directory. The package name is the name of the parent folder.
npm link my-favorite-library adds another symbolic link to the local
In other words, the global
node_modules folder plays a similar role as the npm repository. However, the symbolic links add a live-update feature. Each time you compile your library, it updates the dist folder. The two symbolic links reflect this update to your library.
If you're using a contemporary version of Angular, you might notice this doesn't work. That's because Angular assumes the
node_modules folder hardly ever changes, so it uses caching to speed up compilation. You must deactivate caching, or you'll want to delete the
.angular folder each time you build your library.
Wrapping it up
npm link is a straightforward way to use a library within a monorepo, or to test a library locally before publishing it to the global npm repository or your corporate npm repository, Nexus or Artifactory.