Global Packages
En esta página
Global packages are CLI tools and utilities installed system-wide with pnpm add -g. In pnpm v11, global package management was redesigned for better isolation and reliability.
Installing global packages#
pnpm add -g <pkg>
For example:
pnpm add -g typescript prettier eslint
Isolated installations#
Each globally installed package (or group of packages installed together) gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Isolated installations are stored at {pnpmHomeDir}/global/v11/{hash}/, where the hash is derived from the set of packages installed together.
For example, running the following two commands:
pnpm add -g typescript
pnpm add -g prettier
creates two separate isolated installations — typescript and prettier each get their own node_modules tree and cannot affect each other's dependency resolution.
Installing multiple space-separated packages in a single command also creates a separate isolated install for each one:
pnpm add -g eslint prettier
eslint and prettier each get their own node_modules tree and lockfile and can be removed independently — pnpm remove -g eslint leaves prettier untouched.
To bundle multiple packages into the same isolated install — so they share a node_modules tree and lockfile, resolve peer dependencies against each other, and are removed together — pass them as a comma-separated list:
pnpm add -g eslint,prettier
Here eslint and prettier form a single install group. Removing either with pnpm remove -g removes the whole group.
The two forms can be mixed. For example:
pnpm add -g eslint,prettier typescript
bundles eslint and prettier into one isolated install while installing typescript on its own.
Directory layout#
The contents of {pnpmHomeDir}/global/v11/ look like:
{pnpmHomeDir}/global/v11/
├── {hash-A} → symlink → ./{hash-A-target}/
├── {hash-A-target}/ ← isolated install dir
│ ├── package.json ← lists the packages installed together
│ ├── pnpm-lock.yaml ← lockfile for this install group
│ └── node_modules/
│ ├── <pkg>/ ← top-level dep, symlinked into the global virtual store
│ └── .pnpm/
├── {hash-B} → symlink → ./{hash-B-target}/
├── {hash-B-target}/ ← another isolated install dir
└── store/ ← shared global virtual store
└── ...
- The
{hash}entries are symlinks; pnpm scans for them to enumerate active installs. - The targets are real directories that act as ordinary pnpm projects — each has its own
package.jsonand lockfile. - The shared
store/directory holds the global virtual store. Each install group's direct dependencies — the entries at the root of itsnode_modules/— are symlinks into that store, so the actual package contents are shared rather than copied per group. - Bin shims live in
{pnpmHomeDir}/bin/and point through the appropriate install group'snode_modules.
When a package is removed or its install group is replaced, the hash symlink is updated and orphaned target directories are eventually cleaned up by pnpm store prune.
Listing global packages#
pnpm list -g
pnpm list -g --json # machine-readable
pnpm list -g --parseable # paths only
Because each install group has its own lockfile, listing across multiple groups can only reliably aggregate the top-level packages they were installed with — transitive dependency trees from different groups can't be coherently merged. As a result:
pnpm list -g(default--depth=0) always works and shows every globally installed package.pnpm list -g --depth=<n>(withn > 0) shows the full dependency tree only when:- there is just one global install group, or
- a positional argument narrows the request to a single install group, e.g.
pnpm list -g eslint --depth=1.
If --depth>0 is requested but the request can't be narrowed to a single install group, pnpm errors with ERR_PNPM_GLOBAL_LS_DEPTH_NOT_SUPPORTED.
Managing global packages#
| Command | Description |
|---|---|
pnpm add -g <pkg> |
Install a package globally |
pnpm remove -g <pkg> |
Remove a globally installed package (if it was bundled into an install group, the whole group is removed) |
pnpm update -g [pkg] |
Update global packages (re-installs into new isolated directories) |
pnpm list -g |
List all globally installed packages |
:::note
pnpm install -g (without arguments) is not supported. Use pnpm add -g <pkg> to install specific packages.
:::
Binaries location#
Globally installed binaries are stored in a bin subdirectory of PNPM_HOME (i.e., $PNPM_HOME/bin/). This keeps the PNPM_HOME directory clean — internal directories like global/ and store/ don't pollute shell autocompletion when PNPM_HOME is on PATH.
After upgrading to pnpm v11, run pnpm setup to update your shell configuration so that $PNPM_HOME/bin is on your PATH.
You can check the current global bin directory with:
pnpm bin -g
Global virtual store#
Global installs use the global virtual store. Packages are stored at {storeDir}/links and shared across global installations. This avoids redundant fetches when multiple global packages depend on the same libraries.
Registering local packages globally#
To make a local package's binaries available system-wide, use pnpm add -g . from the package directory:
cd ~/projects/my-tool
pnpm add -g .
This registers the package's bin entries so they can be invoked from anywhere. See pnpm link for more details.
Build script approval#
Global packages that have build scripts (e.g., postinstall) require approval. When you install a global package that needs to run build scripts, pnpm will prompt you to approve or deny the build interactively.
You can also pre-approve builds using the --allow-build flag:
pnpm add -g --allow-build=esbuild esbuild