npm link lets you test local packages without publishing them to a registry.

Imagine you’re building a reusable component library, say my-ui-components, and you’re working on a separate application, my-app, that uses this library. Normally, to test changes in my-ui-components within my-app, you’d have to:

  1. Make changes in my-ui-components.
  2. Run npm publish to upload it to a registry (like npmjs.org or a private one).
  3. Go to my-app and run npm install my-ui-components to get the latest published version.
  4. Repeat.

This is slow, tedious, and can clutter your registry with development versions. npm link bypasses this entirely.

Here’s how it works:

Step 1: Create a Symbolic Link in your Global node_modules

Navigate to the directory of the package you want to link (e.g., my-ui-components). Then, run:

cd ~/projects/my-ui-components
npm link

This command does two things:

  • It creates a global symbolic link (symlink) in your npm’s global node_modules directory that points to your local package’s directory. Think of it like creating a shortcut.
  • It registers the package name and version from its package.json in that global symlink.

After running npm link in my-ui-components, you’ll see output similar to:

/usr/local/lib/node_modules/my-ui-components -> /Users/youruser/projects/my-ui-components

(The exact paths will vary based on your operating system and npm installation.)

Step 2: Link Your Application to the Global Symlink

Now, navigate to the directory of the application that will use your linked package (e.g., my-app). Run:

cd ~/projects/my-app
npm link my-ui-components

This command tells npm to look for a package named my-ui-components in its global node_modules directory and, if found, create a symlink in this project’s local node_modules directory that points to that global symlink.

Your my-app/node_modules directory will now have a my-ui-components folder that is actually a symlink pointing to the global symlink, which in turn points to your local ~/projects/my-ui-components directory.

When you require('my-ui-components') or import { SomeComponent } from 'my-ui-components' in my-app, Node.js will resolve it through these symlinks directly to your local development copy.

See it in Action

Let’s say my-ui-components has a file index.js with:

// ~/projects/my-ui-components/index.js
console.log("UI Components v1.0.0 loaded");
export const Button = () => "<button>Default Button</button>";

And my-app has an app.js that uses it:

// ~/projects/my-app/app.js
import { Button } from 'my-ui-components';

console.log("App is running.");
document.body.innerHTML = Button();

After running npm link in both directories as described, if you cd ~/projects/my-app and run node app.js, you’ll see:

UI Components v1.0.0 loaded
App is running.

Now, if you go back to ~/projects/my-ui-components, change index.js to:

// ~/projects/my-ui-components/index.js
console.log("UI Components v1.0.1 - Updated!");
export const Button = () => "<button>Updated Button</button>";

And then run node ~/projects/my-app/app.js again without publishing or reinstalling anything in my-app, you’ll immediately see:

UI Components v1.0.1 - Updated!
App is running.

The changes are reflected instantly.

The Full Mental Model

npm link is essentially a way to manage symbolic links for your npm packages.

  1. Global Link: npm link in the source package directory (my-ui-components) creates a global symlink. This symlink acts as a central registry for your locally developed packages. It’s like saying, "Here’s where my-ui-components lives globally for testing purposes."
  2. Local Link: npm link <package-name> in the consuming package directory (my-app) creates a symlink in my-app/node_modules that points to the global symlink. This is like saying, "When I ask for <package-name>, use the one you’ve registered globally."

The magic is that Node.js’s module resolution algorithm follows these symlinks. When my-app tries to require('my-ui-components'), it finds the symlink in its node_modules, follows it to the global symlink, and then follows that to the actual source code in ~/projects/my-ui-components. Any change you make in ~/projects/my-ui-components is immediately available to my-app because the symlinks always point to the live, local files.

What if you want to unlink?

To remove the global link:

cd ~/projects/my-ui-components
npm unlink

To remove the link from your application:

cd ~/projects/my-app
npm unlink my-ui-components

This will remove the symlink from my-app/node_modules. If my-app previously had my-ui-components installed from a registry, you would then run npm install my-ui-components to get the published version back.

One common pitfall is forgetting to run npm link inside the package you want to link first. If you try to run npm link my-ui-components in my-app without having run npm link in ~/projects/my-ui-components, npm won’t know where to point the global symlink, and it will likely error or create an empty link.

The next thing you’ll likely run into is dealing with build processes or different Node.js versions when linking packages that have native modules or complex build steps.

Want structured learning?

Take the full Npm course →