pnpm add <pkg>

Installs a package and any packages that it depends on. By default, any new package is installed as a production dependency.

TL;DR#

CommandMeaning
pnpm add saxSave to dependencies
pnpm add -D saxSave to devDependencies
pnpm add -O saxSave to optionalDependencies
pnpm add -g sax Install package globally
pnpm add sax@nextInstall from the next tag
pnpm add sax@3.0.0Specify version 3.0.0
pnpm add lodash@npm:awesome-lodashInstall awesome-lodash as lodash
pnpm add crate:serdeAdd a Cargo crate
pnpm add pypi:httpxAdd a Python package

Supported package sources#

pnpm supports installing packages from various sources. See the Supported package sources page for detailed documentation on:

  • npm registry
  • JSR registry
  • Workspace packages
  • Local file system (tarballs and directories)
  • Remote tarballs
  • Git repositories (with semver, subdirectories, and more)
  • crates.io and PyPI, through the crate: and pypi: prefixes

Package aliases#

To install an npm package under a different local name, use an npm: alias:

pnpm add <alias>@npm:<package>
pnpm add <alias>@npm:<package>@<version-or-tag>

The package after npm: may be scoped and versioned. For example:

pnpm add babel-core@npm:@babel/core@8.0.6

This writes babel-core as the dependency name while resolving @babel/core@8.0.6 as the target package. The npm: part is the npm package-alias protocol, not a named registry; registry routing still follows the target package's default or scope-specific npm registry configuration.

See Aliases for more examples.

Adding a package manager or a runtime#

Added in: v12.0.0-rc.6 (pnpm v12 only)

Naming a package manager (npm, yarn or bun) records which one the project uses instead of installing the npm package that shares the name:

pnpm add yarn@4

writes "packageManager": "yarn@4.18.0". npm and Bun are recorded as a range in devEngines.packageManager instead. Each write removes the other field. Naming a runtime (node, deno) records it under engines.runtime, as the explicit node@tool:22 spelling does since v12.13.0. bun is both, and is declared as the project's package manager unless you ask for the runtime (pnpm add bun@tool:1.3.0).

Globally, pnpm add -g yarn installs the current Yarn line rather than the Classic-only yarn package, and pnpm add -g node@22 installs that Node.js release rather than a wrapper that downloads one.

A specifier that locates a package rather than asking for a released version, such as pnpm add yarn@npm:yarn@1.22.22 or pnpm add yarn@yarnpkg/berry, installs what it names, as an ordinary dependency.

Adding the Rust toolchain#

Added in: v12.11.0

rust names the Rust toolchain, not the npm package of that name. The version is a Rust release channel: a version (1.95.0 or 1.95), stable, beta, nightly, or a dated channel such as nightly-2026-01-01. A bare rust asks for stable.

In a project, pnpm add rust@1.95 pins the toolchain in the rust-toolchain.toml that governs the project, the nearest one at or above it in the workspace. It creates one in the project if there is none, and changes only the channel, keeping the rest of the file. With cargo.enabled, the toolchain is installed too. Nothing is added to package.json.

pnpm add -g rust@stable installs the toolchain globally. The cargo, rustc, and other Rust commands run it outside projects whose rust-toolchain.toml names a toolchain of their own. pnpm update -g rust installs the newest release of its channel, pnpm ls -g lists it, and pnpm remove -g rust removes it.

Protocol-prefixed selectors#

A selector may carry the protocol in front of the name rather than after it:

pnpm add jsr:@scope/pkg
pnpm add npm:pkg@^1.0.0
pnpm add workspace:pkg@*

Package URLs#

Added in: v12.5.0

pnpm add accepts Package URLs for npm, Cargo, and Python packages:

pnpm add pkg:npm/express@4.18.2
pnpm add pkg:cargo/serde@1.0.188
pnpm add pkg:pypi/requests@2.31.0

These save dependencies to package.json, Cargo.toml, and pyproject.toml, respectively. Cargo and Python use their ecosystem configuration. pkg is a reserved specifier prefix, regardless of case, and cannot be used as a named registry alias.

Options#

--save-prod, -P, -p#

Install the specified packages as regular dependencies.

--save-dev, -D, -d#

Install the specified packages as devDependencies.

--save-optional, -O, -o#

Install the specified packages as optionalDependencies.

--save-exact, -E, -e#

Saved dependencies will be configured with an exact version rather than using pnpm's default semver range operator.

--save-build#

Install crate: packages as Cargo build dependencies.

--save-peer#

Using --save-peer will add one or more packages to peerDependencies and install them as dev dependencies. --no-save-peer overrides a savePeer: true setting.

--save-types#

Added in: v12.6.0

Saves available @types/* packages in devDependencies alongside registry dependencies. Packages that declare bundled TypeScript types are skipped.

pnpm add express --save-types
# saves express to dependencies, @types/express to devDependencies

Set saveTypes: true in pnpm-workspace.yaml to enable this by default. --no-save-types overrides that setting.

--save-prefix <prefix>#

The prefix of the saved version range: ^, ~, = for an explicit exact pin, or an empty string for a bare exact version. Overrides the savePrefix setting.

--tilde#

Added in: v12.6.0

Alias for --save-prefix=~. Saved dependencies will be configured with a tilde range (~1.2.3) instead of the default caret range (^1.2.3).

--save-catalog#

Save the new dependency to the default catalog.

--save-catalog-name <catalog_name>#

Save the new dependency to the specified catalog.

--config#

Save the dependency to configDependencies.

Since v12.12.0, the saved range follows the same rules as for a dependency in package.json.

--ignore-workspace-root-check#

Adding a new dependency to the root workspace package fails, unless you pass --ignore-workspace-root-check or target the root with -w (--workspace-root).

For instance, pnpm add debug -w.

--global, -g#

Install a package globally. See Global Packages for details.

Each space-separated package is installed into its own isolated directory. To bundle several packages into a single isolated install (so they share dependencies and are removed together), pass them as a comma-separated list, e.g. pnpm add -g eslint,prettier.

--workspace#

Only adds the new dependency if it is found in the workspace. The dependency is saved with the workspace: protocol and linked from the workspace project that provides it. When no workspace project provides the package, the command fails instead of falling back to the registry.

--allow-build#

A list of package names that are allowed to run postinstall scripts during installation.

Example:

pnpm --allow-build=esbuild add my-bundler

This will run esbuild's postinstall script and also add it to the allowBuilds field of pnpm-workspace.yaml. So, esbuild will always be allowed to run its scripts in the future.

Since v12.4.0, prefixing a name with ! denies the build instead:

pnpm add --allow-build=!core-js my-bundler

writes allowBuilds: { core-js: false }, so the package is never asked about again. Global installs (pnpm add -g) record the denial too.

--lockfile-only#

Update package.json and pnpm-lock.yaml without downloading dependencies or writing node_modules.

--force#

Reinstall every package the lockfile names, relinking packages that an earlier install already linked.

--lockfile-dir <dir>#

The directory in which pnpm-lock.yaml is created. Same as the lockfileDir setting.

--prod, --production#

Leave devDependencies out of node_modules.

--dev#

Leave dependencies out of node_modules.

--[no-]optional#

Include or exclude optionalDependencies from node_modules.

--[no-]auto-dedupe#

Enable or disable the autoDedupe setting for this install.

--ignore-scripts#

Do not run the lifecycle scripts of the project or its dependencies. Same as the ignoreScripts setting.

--ignore-pnpmfile#

Disable the pnpmfile hooks, including the pnpmfiles of config dependencies.

--registry#

Specify the registry to use for this installation:

pnpm add --registry=https://registry.npmjs.org/ lodash

--filter <package_selector>#

Read more about filtering.

--cpu=<name>#

Override CPU architecture of native modules to install. Acceptable values are same as cpu field of package.json, which comes from process.arch.

--os=<name>#

Override OS of native modules to install. Acceptable values are same as os field of package.json, which comes from process.platform.

--libc=<name>#

Override libc of native modules to install. Acceptable values are same as libc field of package.json.