npm install downloads and sets up the packages your project needs
When you run npm install in a project folder, npm reads a file called package.json that lists every package your project depends on. It then downloads those packages from the npm registry (a central online library), puts them in a folder called node_modules, and arranges them so your code can find and use them. If you already have a package-lock.json file, npm uses that to read the exact same versions that were installed before, so everyone working on the project gets identical packages.
You typically run npm install when you first clone a project from GitHub, when you add a new package to the project, or when you pull changes that someone else made to the package list. Without running it, your project folder will be missing the code it needs to run.
Key Takeaways
- npm install reads your package.json file and downloads all the packages listed there into a node_modules folder.
- The command also creates or updates package-lock.json, which locks packages to specific versions so the same code runs on every machine.
- You need to run npm install after cloning a project, after someone else updates package.json, or after you add a new package with npm install package-name.
- The node_modules folder can be very large and is normally not stored in version control — npm install rebuilds it from package.json whenever needed.
What happens inside node_modules
When npm install finishes, it creates a folder called node_modules in your project. This folder contains not just the packages you asked for, but also all of their dependencies — the packages they need to work. A single package might depend on dozens of others, and those might depend on more, so node_modules can grow to thousands of files and hundreds of megabytes.
Your code finds packages by name. When you write const express = require('express') or import express from 'express', Node.js looks in node_modules for a folder called express and loads the code from there. npm install organizes everything so this lookup works automatically.
Because node_modules is so large and gets rebuilt the same way every time, it is normally added to .gitignore so it does not get stored in version control. When someone clones your project, they run npm install to rebuild node_modules from scratch using your package.json and package-lock.json files.
The difference between package.json and package-lock.json
package.json is the file you edit. It lists the packages your project needs and usually specifies a version range — for example, "express": "^4.18.0". The caret (^) means npm can install version 4.18.0 or any newer version up to 5.0.0. This flexibility lets you get bug fixes automatically.
package-lock.json is created and updated by npm install. It locks every package to an exact version number — for example, "express": "4.18.2" with no range. When you or a teammate run npm install later, npm reads package-lock.json and downloads those exact versions, ensuring everyone has identical code. This prevents surprises when a package releases a new version that breaks something.
You should commit package-lock.json to version control so your team stays in sync. You should not edit it by hand — npm manages it automatically when you run npm install or add packages with npm install package-name.
When to run npm install and when to use npm install package-name
Run npm install with no package name in these situations: after you clone a project, after you pull changes that updated package.json, or after a teammate tells you they added a new dependency. The command reads package.json and downloads everything listed there.
Run npm install package-name when you want to add a new package to your project. For example, npm install lodash downloads lodash, adds it to package.json, and updates package-lock.json. This is how you bring new tools into your project during development.
Some packages are only needed during development — testing tools, build tools, linters — not when your code runs in production. You can install these with npm install --save-dev package-name, which puts them in a separate section of package.json. When someone deploys your code to a server, they run npm install --production to skip these development-only packages and keep the deployment smaller and faster.
Why npm install can take time on the first run
The first time you run npm install on a project, it can take several minutes. npm has to read potentially hundreds of packages from the internet, verify that they are not corrupted, and organize them in node_modules. Subsequent runs are faster because npm caches downloaded packages locally, so it does not have to fetch them again.
If npm install seems stuck, it is usually downloading a large package or waiting for the registry to respond. You can see what it is doing by running npm install --verbose, which prints detailed output. If it fails partway through, running npm install again usually picks up where it left off.
On very slow internet or with very large projects, npm install can time out. You can increase the timeout with npm install --fetch-timeout=120000 (the number is milliseconds). If you hit repeated problems, your team might use npm ci instead, which is faster and stricter — it installs exactly what package-lock.json specifies and fails if package.json and package-lock.json do not match.
What npm install does not do
npm install downloads and organizes packages, but it does not run your code or start your project. After npm install finishes, you typically run a command like npm start or npm run dev to actually start your process. These commands are defined in package.json under a "scripts" section and usually start a development server or build tool.
npm install also does not update packages to newer versions. If you want to upgrade a package, you run npm update package-name or npm install package-name@latest. Running plain npm install uses the versions already locked in package-lock.json.
Troubleshooting npm install problems
If npm install fails with a permission error on macOS or Linux, you may have installed Node.js in a way that requires sudo. Do not run npm with sudo — instead, fix your Node.js installation. The Node.js website has instructions for installing without permission issues.
If npm install says a package is missing or incompatible, check that package.json is not corrupted and that you are in the correct project folder. You can also try deleting node_modules and package-lock.json, then running npm install again to start fresh. This solves most version conflicts.
If npm install works but your code still cannot find a package, make sure you are using the correct package name. Package names are case-sensitive, and some packages use hyphens or underscores in ways that are straightforward to mistype. Check the npm registry website (npmjs.com) to confirm the exact name.
Frequently Asked Questions
Do I need to run npm install every time I start working on a project?
No. You run npm install once after cloning a project or pulling changes that updated package.json. After that, node_modules stays on your machine and your code can use it. You only run npm install again when dependencies change.
What is the difference between npm install and npm ci?
npm install is flexible — it reads package.json and updates package-lock.json if versions have changed. npm ci (continuous integration) is strict — it installs exactly what package-lock.json specifies and fails if the two files do not match. Teams use npm ci in automated deployments to may support consistency.
Can I delete node_modules to save space?
Yes. node_modules can be deleted safely because npm install rebuilds it from package.json and package-lock.json. This is useful if you are not actively working on a project and want to free up disk space. Run npm install again when you need to work on it.
Why is node_modules so large?
Each package can depend on many other packages, and those depend on more. A single top-level package might pull in hundreds of dependencies. npm also keeps separate copies of packages when different versions are needed, which adds to the size. This is why node_modules is not stored in version control.
What if two packages need different versions of the same dependency?
npm handles this by nesting packages inside each other's node_modules folders. If package A needs version 1 of a tool and package B needs version 2, npm puts version 1 inside A's folder and version 2 inside B's folder. Your code gets the right version depending on which package is using it.