npm and package.json
Dependencies vs devDependencies, semver and ranges, the lock file, commands.
Updated
What npm is
npm is two things: an online registry with over 3 million JavaScript packages (libraries written by others) and the terminal program you install them with. When you type npm i react, npm downloads React and every package React depends on into the node_modules folder.
Alternatives to the npm program, using the same registry: pnpm (fast, saves disk space), yarn, bun. You pick one per project — the lock file shows which.
package.json — the project's ID card
{
"name": "learn-js",
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
"lint": "eslint"
},
"dependencies": {
"next": "16.4.0",
"react": "19.3.0"
},
"devDependencies": {
"typescript": "^5",
"eslint": "^9"
}
}| Field | Role |
|---|---|
scripts |
commands run with npm run <name> (dev, build, test) |
dependencies |
packages the app needs to run (react, next, zod) |
devDependencies |
packages needed only in development / build (typescript, eslint, vitest, @types/* types) |
type: "module" |
.js files are ES modules (import/export) |
engines |
which Node version is required |
Versions: semver and ranges
Versions follow semver: MAJOR.MINOR.PATCH. In package.json you write a range — which versions you accept:
| Range | Accepts | Example for 1.4.2 |
|---|---|---|
1.4.2 |
exactly this version | only 1.4.2 |
^1.4.2 |
the same major — new minors and patches | 1.4.2 … 1.99.x, not 2.0.0 |
~1.4.2 |
the same minor — patches only | 1.4.2 … 1.4.x |
* / latest |
anything | dangerous |
^ is the default with npm i. For applications, many teams prefer exact versions (npm i -E) + controlled updates (Renovate, Dependabot).
The lock file
package-lock.json (or pnpm-lock.yaml) records the exact version of every installed package, including indirect dependencies. The result: you, your teammates, CI and Vercel all install exactly the same thing.
| Rule | Why |
|---|---|
| the lock file goes into Git | identical installs everywhere |
node_modules doesn't go into Git |
it's rebuilt from the lock file |
in CI you use npm ci |
installs strictly from the lock file, fails if it doesn't match |
Everyday commands
| Command | What it does |
|---|---|
npm i |
installs everything from package.json / the lock file |
npm i zod / npm i -D vitest |
adds a dependency / a devDependency |
npm run dev |
runs the dev script |
npx <package> |
runs a program without installing it globally (npx create-next-app) |
npm outdated |
which packages have new versions |
npm uninstall <package> |
removes it |
npm audit |
known vulnerabilities |
How to choose a package
Before npm i, check: is it maintained (a recent release)? how many weekly downloads does it have? how big is it (bundlephobia / pkg-size)? does it have TypeScript types? And the best question: do I really need it? — Intl, structuredClone, fetch, crypto.randomUUID are native.
Summary
package.json= scripts + dependencies;devDependenciesonly for development.^accepts minors and patches;~only patches; exact = no surprises.- The lock file in Git,
node_modulesnever;npm ciin CI.