Install and manage dependencies with npm, pnpm and yarn. Understand package.json, lockfiles, semantic versioning and scripts.

Bishal Biswas
Self Learner
What a package manager does
It downloads code other people wrote, keeps track of exactly which versions you are using, and makes sure everyone on your team gets the same thing.
npm comes bundled with Node.js, so if you have Node you already have npm.
Starting a project
npm init
npm init -y
That creates package.json, the file describing your project.
{
"name": "my-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "vitest"
},
"dependencies": {
"axios": "^1.7.0"
},
"devDependencies": {
"vite": "^5.4.0"
}
}
"type": "module" tells Node to treat .js files as ES modules, so import works instead of require.
Installing
npm install axios
npm install --save-dev vitest
npm install --global typescript
npm install
The difference matters:
- dependencies are needed for the app to run.
- devDependencies are only needed while developing, such as test runners and bundlers.
Getting this wrong bloats production installs, or breaks them when a runtime dependency was filed as a dev one.
Semantic versioning
{
"axios": "1.7.2"
}
Read MAJOR.MINOR.PATCH:
- PATCH (1.7.2 to 1.7.3) is a bug fix, safe.
- MINOR (1.7.2 to 1.8.0) adds features without breaking anything, safe.
- MAJOR (1.7.2 to 2.0.0) contains breaking changes, so read the notes.
The symbol in front changes what is allowed:
{
"caret": "^1.7.2",
"tilde": "~1.7.2",
"exact": "1.7.2",
"range": ">=1.7.2 <2.0.0"
}
^allows minor and patch updates. This is the default.~allows patch updates only.- No symbol means exactly that version.
The lockfile
package-lock.json records the exact version of every package, including the dependencies of your dependencies.
git add package-lock.json
Always commit the lockfile. Without it, two people installing on different days can get different versions, and you get the classic “it works on my machine”.
npm ci
npm ci installs strictly from the lockfile, ignoring the ranges in package.json. It is faster and reproducible, which is why it is what you want in CI pipelines.
Scripts
{
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "vitest",
"lint": "eslint src",
"format": "prettier --write src"
}
}
npm run dev
npm test
npm run lint
test, start and a few others can drop the run. Everything else needs it.
Scripts can chain:
{
"scripts": {
"build": "npm run clean && vite build",
"clean": "rm -rf dist",
"prebuild": "npm run lint"
}
}
Anything named preX runs automatically before X, and postX runs after.
Scripts can use locally installed binaries directly, without a global install. That is why "dev": "vite" works.
npx
npx create-vite@latest my-app
npx eslint src
npx runs a package without installing it permanently. Perfect for one off tools like project generators.
Updating
npm outdated
npm update
npm install axios@latest
npm install axios@1.7.2
npm update respects your version ranges, so it will not jump a major version. For that you must ask explicitly, and read the changelog first.
Auditing
npm audit
npm audit fix
npm audit fix --force
Be careful with --force. It can install breaking major versions to resolve an advisory. Read what it plans to do.
Not every advisory matters equally. A vulnerability in a dev only tool that never touches user input is not the same as one in your server framework.
Removing
npm uninstall axios
npm prune
Fewer dependencies is genuinely better. Every package is code you ship, code you must trust, and a potential security advisory. Before installing something, ask whether ten lines of your own would do.
npm, yarn or pnpm?
All three read package.json and do the same job.
npm ships with Node and needs no setup. The default choice.
yarn was created when npm was slow and lacked a lockfile. Modern yarn has Plug’n’Play, which skips node_modules entirely.
pnpm stores one copy of each package version on your machine and links to it. Much less disk space, faster installs, and it is stricter about undeclared dependencies, which catches real bugs.
npm install
yarn install
pnpm install
npm run dev
yarn dev
pnpm dev
Pick one per project and commit only its lockfile. Mixing them produces conflicting lockfiles and confusing bugs. Note them in your README so nobody guesses.
Monorepo workspaces
{
"name": "my-monorepo",
"private": true,
"workspaces": ["packages/*"]
}
Workspaces let several packages share one install and reference each other locally. All three managers support them.
Publishing your own
npm login
npm publish
npm version patch
npm publish
Before publishing, check what will actually be included:
npm pack --dry-run
Use the files field in package.json, or a .npmignore, so you do not ship tests and source maps to everyone.
Quick recap
package.jsondescribes your project, dependencies and scripts.- Runtime code goes in
dependencies, tooling indevDependencies. ^allows minor updates,~allows patches only.- Commit the lockfile, and use
npm ciin CI for reproducible installs. npm runexecutes scripts, andpreandposthooks run automatically.npxruns a tool without installing it.- Pick one package manager per project and stick to it.
{
"name": "my-app",
"version": "1.0.0",
"type": "module",
"description": "type: module makes .js files ES modules, so import works",
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "vitest",
"lint": "eslint src",
"prebuild": "npm run lint",
"clean": "rm -rf dist"
},
"dependencies": {
"axios": "^1.7.0"
},
"devDependencies": {
"vite": "^5.4.0",
"vitest": "^2.0.0",
"eslint": "^9.0.0"
},
"engines": {
"node": ">=20"
},
"files": ["dist"]
}
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready