NodeJSGlobal vs Local Packages

Global Packages

A global install (npm install -g) places a package's executables on your system PATH so you can run them from any directory — not tied to one project. It is the right tool for daily-driver CLIs, and the wrong tool for project dependencies. Knowing the difference avoids the classic "works on my machine" trap.

Local vs global

Local (default)

Global (-g)

Installs to

./node_modules

A system-wide prefix

Recorded in package.json

Yes

No — invisible to your project

Available from

The project (and its scripts)

Anywhere on your machine

Reproducible across machines

Yes (via lockfile)

No

Use for

Everything a project depends on

Personal CLIs you run constantly

Installing and managing globals

Bash
npm install -g pnpm typescript     # install global CLIs
npm list -g --depth=0              # list what you've installed globally
npm outdated -g                    # which globals have updates
npm update -g                      # update all globals
npm uninstall -g typescript        # remove one
npm root -g                        # print the global node_modules path
npm prefix -g                      # print the global install prefix
$ npm list -g --depth=0
/usr/local/lib
├── npm@10.5.0
├── pnpm@9.1.0
└── typescript@5.4.5
Where globals actually live

Global binaries land in a bin directory that is on your PATH; the package code sits in a global node_modules. The exact location depends on how Node was installed:

Typical global prefixes

Text
nvm-managed Node :  ~/.nvm/versions/node/<ver>/bin   ← changes per Node version!
Homebrew (mac)   :  /opt/homebrew/bin  or  /usr/local/bin
Linux (system)   :  /usr/local/bin   (may need sudo — see warning)
Windows          :  %AppData%\npm
With nvm, globals are per Node version
If you use [nvm](/nodejs/nvm), each Node version has its *own* global `node_modules`. Switch from Node 20 to Node 22 and your globally-installed `typescript` is suddenly "missing" — it was installed under 20. Either reinstall it under the new version, or use `nvm install <ver> --reinstall-packages-from=<oldver>` to carry globals across.
Never use sudo to fix permission errors
`sudo npm install -g` is a trap
On a system Node, `npm install -g` may hit `EACCES` permission errors. The tempting fix — `sudo npm install -g` — runs arbitrary package `postinstall` scripts **as root**, a serious security hole, and leaves root-owned files that break later non-sudo installs. The correct fix is to own your global prefix: use a version manager like nvm/fnm (recommended), or reconfigure npm's prefix to a directory you own (`npm config set prefix ~/.npm-global` and add its `bin` to `PATH`).
What belongs global — and what does not
  • Global is fine for: version managers (pnpm, nodemon), personal scaffolding helpers, system utilities you invoke from any folder.

  • Never global for project deps: a teammate cloning your repo has no idea you -g installed something — it is not in package.json, so their build fails.

  • Prefer npx for one-offs: scaffolders and rarely-used tools should run via npx, not clutter your global space with stale versions.

  • Prefer local + scripts for build tools: typescript, eslint, jest belong in devDependencies so the version is pinned and shared.

The modern default: install less globally
Between local `devDependencies` (pinned, shared, reproducible) and `npx` (zero-install one-offs), there is little left that *needs* to be global. A lean global list — basically your version manager and maybe one or two daily CLIs — is a sign of a healthy setup.
Next
Keep your dependencies current and safe over time: [Updating Packages](/nodejs/updating-packages).