Adding a library to your project is the first command you will repeat a thousand times. This lesson covers the syntax for npm, pnpm, and bun, and the distinction between regular and development dependencies.
The basic command
With pnpm (recommended):
With bun:
With npm:
All three commands do the same thing: download the package, add it to dependencies in package.json, and update the lockfile.
Development dependencies
Some packages are only useful during development: TypeScript compilers, linters, test frameworks. You do not want them in production. Use the -D flag (or --save-dev):
These packages land in devDependencies in package.json instead of dependencies.
Understanding semver versioning
When you install a package, package.json records the version with a prefix:
{
"dependencies": {
"react": "^18.3.0",
"lodash": "~4.17.21",
"some-lib": "2.1.0"
}
}^18.3.0(caret): accepts any18.x.xversion greater than or equal to18.3.0. Minor and patch updates are allowed.~4.17.21(tilde): accepts4.17.xonly. Only patch updates are allowed.2.1.0(exact): this precise version, nothing else.
The caret ^ is the default for pnpm, bun, and npm. It works for most cases.
Installing global packages
For a CLI tool you want available everywhere (e.g., vercel, http-server):
Prefer pnpm dlx (or npx) for one-off commands instead of global installs:
Reinstalling all dependencies
When you clone an existing project, you have no node_modules/. Just run:
pnpm reads the lockfile and installs exactly the same versions as the project author.
npm semantic versioning