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:
- Make changes in
my-ui-components. - Run
npm publishto upload it to a registry (like npmjs.org or a private one). - Go to
my-appand runnpm install my-ui-componentsto get the latest published version. - 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_modulesdirectory 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.jsonin 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.
- Global Link:
npm linkin 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 wheremy-ui-componentslives globally for testing purposes." - Local Link:
npm link <package-name>in the consuming package directory (my-app) creates a symlink inmy-app/node_modulesthat 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.