- 4 minutes read

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. Image published by BeyondJava (entire sketch) / Peggy_Marco (images of the persons) on BeyondJava / Pixabay under a Pixaybay license.

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 main entry:

{ "name": "ABC", "version": "1.0.0", "main": "index.js",

More modern libraries may use module instead of main. Using module activates the ESM module system, producing more compact binaries.

The second file you need is the JavaScript file itself.

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 dist/my-favorite-library.

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:

cd dist/my-favorite-library npm link

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:

npm link my-favorite-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 node_modules folder.

How does it work?

If you've read until this point, you're probably a JavaScript developer, and you are familiar with the two classes of node_modules folders:

  • The local node_modules folder belongs to your application. Every application has its copy of the node_modules folder, each populated with different libraries.
  • The global node_modules folder contains infrastructure like the Angular, React, and Vue CLI tools. These commands and libraries are available everywhere, even outside JavaScript projects. This allows you to use commands like ng new or create-react-app.

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 node_modules folder.

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.

Disable caching!

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.