Skip to content

/commands/yarn/yarn-add-dev-dependency

yarn add --dev: add a devDependency with Yarn (classic and Berry)

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
tool
Yarn
versions
Yarn 1.x, Yarn 2+, Yarn 4
os
any

yarn add --dev typescript (short form yarn add -D typescript) installs a package and records it under devDependencies in package.json, for tools you need while developing and testing but not at run time: test runners, linters, compilers, type definitions. The flag is the same in Yarn 1 (classic) and Yarn 2+ (Berry); what differs is how each one saves the version and lays out the install.

Yarn 1 (classic)

bash
echo '{ "name": "app", "version": "1.0.0", "license": "MIT", "private": true }' > package.json
npx --yes yarn@1 add --dev --silent --no-progress is-number@6.0.0
cat package.json
ls -d node_modules/is-number yarn.lock

Output

{
  "name": "app",
  "version": "1.0.0",
  "license": "MIT",
  "private": true,
  "devDependencies": {
    "is-number": "6.0.0"
  }
}
node_modules/is-number
yarn.lock

Yarn 1 saves the specifier you typed (here the exact 6.0.0; without a version it writes a caret range such as ^7.0.0), installs into node_modules and writes yarn.lock. The example runs Yarn through npx yarn@1; with Yarn installed the command is yarn add --dev is-number@6.0.0.

Yarn 4 (Berry)

bash
echo '{ "name": "app", "version": "1.0.0", "license": "MIT", "private": true }' > package.json
touch yarn.lock
npx --yes @yarnpkg/cli-dist@4 add -D is-number@6.0.0 > /dev/null
cat package.json
ls -a | grep -E "^(\.pnp\.cjs|\.yarn|yarn\.lock)$"

Output

{
  "name": "app",
  "version": "1.0.0",
  "license": "MIT",
  "private": true,
  "devDependencies": {
    "is-number": "6.0.0"
  }
}
.pnp.cjs
.yarn
yarn.lock

The empty yarn.lock marks the folder as its own Yarn project. Yarn 4 saves the same entry, but by default uses Plug'n'Play: instead of node_modules it writes .pnp.cjs and keeps packages as zip files in a cache. Set nodeLinker: node-modules in .yarnrc.yml if your tools need a classic node_modules folder.

Options

CommandWhat it does
yarn add -D jestdevDependencies (same in Yarn 1 and 2+)
yarn add -D -E prettierSave the exact version instead of a caret range (--exact)
yarn add -D -T eslintSave a tilde range, patch updates only (--tilde)
yarn add -P reactpeerDependencies (--peer)
yarn add -O fseventsoptionalDependencies (--optional)
yarn add -D @types/node@22A specific major version of the type definitions
yarn workspace web add -D viteAdd to one workspace package (Yarn 1 and 2+)
yarn remove jestRemove the package again

Pitfalls

  • Global installs. Yarn 2+ has no yarn global add; use yarn dlx tool for one-off commands, or install the tool with npm. Yarn 1 still has yarn global add.
  • Workspace root. In a Yarn 1 workspace, yarn add -D pkg at the root fails unless you confirm with -W (--ignore-workspace-root-check).
  • Lockfile in CI. After adding a package, commit yarn.lock too. CI runs yarn install --immutable (Yarn 2+) or --frozen-lockfile (Yarn 1) and fails when the lockfile is out of date; see package not present in the lockfile.
  • Wrong Yarn version. yarn --version shows which Yarn runs in that folder; projects pin it in the packageManager field of package.json. The Yarn classic vs Berry comparison lists the commands that differ.

How to confirm it worked

cat package.json shows the package under devDependencies, and yarn why package shows that it is installed and which version was resolved. On Yarn 2+, yarn info package prints its resolved version too.

FAQ

What is the difference between yarn add -D and yarn add --dev?

None. -D is the short form of --dev in both Yarn 1 and Yarn 2+. Both write the package to devDependencies in package.json.

Does yarn install skip devDependencies in production?

In Yarn 1, yarn install --production (or NODE_ENV=production) skips them. Yarn 2+ has no --production flag; use yarn workspaces focus --production, which needs the workspace-tools plugin on Yarn 2 and 3 and is built in from Yarn 4.

Why does yarn add say the package is not present in the lockfile?

That error usually comes from a later yarn install --immutable in CI when package.json and yarn.lock disagree. Run yarn install locally, commit yarn.lock, and the check passes.

Sources