The complete npm guide — package.json, installing and updating packages, semantic versioning, package-lock.json, scripts, workspaces, auditing, and publishing.
Why: real apps depend on hundreds of packages, which depend on other packages. A package manager resolves, downloads, and wires this whole tree for you. npm is bundled with Node.js and installs from the public npm registry.
node --versionnpm --versionWhy: package.json is the manifest of your project — its name, version, dependencies, and scripts. Every project starts here. Note: you only do this for projects you start from scratch — frameworks like Next.js generate package.json for you when you scaffold the project.
Interactive — asks questions
npm initAccept all defaults
npm init -yNote: you will edit this file constantly — these are the fields that matter day to day. "type": "module" makes .js files use import/export (ESM) instead of require(); new projects should use it.
A typical package.json
{
"name": "my-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
"test": "vitest"
},
"dependencies": {
"react": "^19.0.0"
},
"devDependencies": {
"typescript": "^5.6.0"
},
"engines": {
"node": ">=20"
}
}npm can edit fields for you
npm pkg set type=moduleWhy: dependencies are packages your app needs at runtime; devDependencies are tools only needed while developing — test runners, linters, bundlers. Production installs can skip devDependencies entirely.
Runtime dependency
npm install reactDev-only dependency (-D is short for --save-dev)
npm install -D typescriptProduction install — skips devDependencies
npm install --omit=devWhy: after cloning a project, npm install downloads everything into node_modules. outdated shows current vs wanted (allowed by your range) vs latest.
Install all dependencies from package.json
npm installnpm install "package"npm uninstall "package"What can be updated?
npm outdatedUpdate within your semver ranges
npm updateWhy: global installs are for CLI tools you use across projects (not project dependencies). npx runs a package binary without installing it at all — perfect for scaffolding tools you use once.
npm install -g "package"List what's installed globally
npm list -g --depth=0Run without installing
npx create-next-app@latestWhy: semver is a contract — breaking changes bump major, new features bump minor, bug fixes bump patch. ^19.2.1 accepts any 19.x.x (>= 19.2.1), ~19.2.1 only 19.2.x, and 19.2.1 alone means exactly that version. The caret is the default — this is why installs can differ without a lockfile.
19.2.1 19 = major: breaking changes 2 = minor: new features, backwards compatible 1 = patch: bug fixes only
Default — caret range
npm install reactExact version, no range
npm install react@19.2.1 --save-exactInstall a specific older major
npm install react@18Why: package.json says "react: ^19.0.0" — a range. package-lock.json records the exact version of every package in the tree, including dependencies of dependencies, so every install resolves identically. Always commit it.
git add package-lock.jsongit commit -m "Update lockfile"Why: the scripts field is the standard place for project commands — anyone can clone your repo and know that dev, build, and test will work. A script named prebuild runs automatically before build, postbuild after it. Everything after -- goes to the underlying command.
npm run devnpm run buildtest and start get shortcuts
npm testnpm startList all available scripts
npm runPass arguments through
npm run test -- --watchWhy: a workspace is multiple packages in one repo — apps and shared libraries — sharing a single lockfile and install.
Declare in package.json: "workspaces": ["apps/*", "packages/*"]
One install at the root wires everything
npm installTarget one workspace, or all of them
npm run build --workspace=apps/webnpm run build --workspacesNote: audit checks your tree against a database of known vulnerabilities. Typosquatting and compromised packages are real — check what you install, and remember postinstall scripts run arbitrary code on your machine.
npm auditOnly fail on serious issues (useful in CI)
npm audit --audit-level=highApply safe updates within your ranges
npm audit fixInstall without running any package's lifecycle scripts
npm install --ignore-scriptsWhy: .npmrc controls npm per project (committed, shared with the team) or per user (~/.npmrc) — registries, exact saving, engine enforcement.
See effective config and where it comes from
npm config listAlways save exact versions instead of ^ ranges
npm config set save-exact=trueMake the engines field an error, not a warning
npm config set engine-strict=true