What Is npm install? A Clear Guide to How It Works
If you've ever started working on a JavaScript or Node.js project, there's a good chance one of the first things you were told to do was run npm install. It's one of the most commonly typed commands in web development — and understanding what it actually does makes you a better, more confident developer.
What npm Is and Why It Exists
npm stands for Node Package Manager. It's the default package manager that ships with Node.js, and it gives developers access to a massive public registry of reusable code packages — everything from utility libraries and testing frameworks to full-featured front-end tools.
Instead of writing every feature from scratch, developers use packages (sometimes called dependencies) created and maintained by other developers. npm makes it easy to install, manage, update, and remove those packages in your project.
What npm install Actually Does
When you run npm install in a project directory, npm reads a file called package.json. This file lists all the packages your project depends on, along with their required version ranges.
Here's what happens step by step:
- npm reads the
package.jsonfile in your current directory - It checks whether a
package-lock.jsonfile exists (more on that below) - It downloads all listed dependencies from the npm registry
- It places those packages inside a folder called
node_modules
After the command completes, your project has access to all the external code it needs to run properly.
package.json vs package-lock.json
These two files work together but serve different purposes:
| File | Purpose |
|---|---|
package.json | Lists your project's intended dependencies and version ranges |
package-lock.json | Records the exact versions that were installed |
The package-lock.json file ensures that every developer working on the same project installs identical versions of every dependency — not just compatible ones. This prevents the frustrating "it works on my machine" problem. 🔧
If a package-lock.json exists, npm uses it as a precise blueprint. If it doesn't exist yet, npm creates one on the first install.
Dependencies vs devDependencies
Your package.json typically separates packages into two categories:
- dependencies — Packages required for your application to run in production (e.g., React, Express, Axios)
- devDependencies — Packages only needed during development (e.g., testing tools like Jest, bundlers like Webpack, linters like ESLint)
By default, npm install installs both categories. If you're deploying to a production server and want to skip devDependencies, you'd use npm install --production instead. This is relevant for keeping production environments lean and secure.
Common Variations of the Command
npm install isn't one-size-fits-all. Here are the most frequently used forms:
| Command | What It Does |
|---|---|
npm install | Installs all dependencies listed in package.json |
npm install <package-name> | Installs a specific package and adds it to dependencies |
npm install <package-name> --save-dev | Installs a package and adds it to devDependencies |
npm install -g <package-name> | Installs a package globally on your system |
npm install --production | Skips devDependencies during install |
npm ci | Clean install using package-lock.json only — common in CI/CD pipelines |
The -g flag is particularly important to understand. A global install makes a package available anywhere on your machine, not just in one project. Tools like nodemon, typescript, and create-react-app are often installed globally because they're used across multiple projects.
What node_modules Contains
The node_modules folder can grow surprisingly large — often tens of thousands of files. That's because packages frequently depend on other packages (called transitive dependencies), and npm installs the entire chain.
This is why node_modules is almost always added to .gitignore. There's no need to commit it to version control because anyone who clones your repository can regenerate it simply by running npm install. The package.json and package-lock.json files are all that's needed to recreate it exactly. 📁
What Can Go Wrong
A few common issues developers run into:
- Peer dependency warnings — Some packages expect you to have a specific version of another package already installed. npm will warn you but may not auto-install these.
- Permission errors — On macOS and Linux, global installs may require elevated permissions. This is often solved by configuring npm's default directory rather than using
sudo. - Version conflicts — Two packages requiring incompatible versions of a shared dependency can cause resolution errors.
- Corrupted cache — Occasionally, a stale npm cache causes unexpected behavior. Running
npm cache clean --forcecan resolve this.
How Your Setup Affects the Experience
The experience of running npm install varies quite a bit depending on your environment:
- Project size — A simple utility script may install a handful of packages in seconds. A large React or Next.js project can involve hundreds of nested dependencies.
- Node.js and npm version — Older versions of npm handle dependency resolution differently than newer ones. npm v7 and later introduced automatic peer dependency installation, which changed behavior noticeably.
- Network speed and registry latency — npm fetches packages from its remote registry by default, so connection quality matters for large installs.
- Operating system — Some packages include native binaries compiled for specific platforms. A package that installs smoothly on macOS may require build tools on Windows or Linux.
- Use of alternative package managers — Developers working in teams sometimes use Yarn or pnpm instead of npm. These tools read the same
package.jsonformat but manage lockfiles and caching differently, which affects install speed and disk usage.
Whether npm install is the right tool, or whether a different package manager or workflow better fits your project, depends on the specifics of your stack, your team's conventions, and what you're trying to build.