Dependencies vs devDependencies

Two lists, one simple question

Your package.json keeps packages in two groups. To decide which group a package belongs in, ask one question: "Does the finished, shipped project need this to work, or do I only need it while building?"

  • dependencies — needed to build or run the actual project.
  • devDependencies — only needed during development (testing, type-checking, deploying, generating images).

This project's real split

Here's how this very website divides its packages — a useful real-world example:

dependencies (needed to build the site)

  • astro — the framework that generates the website. Without it, there's no site.
  • tailwindcss & @tailwindcss/vite — produce all the styling.
  • three — the 3D library the engine visualisation needs to run in the browser.

devDependencies (helpful tools only)

  • typescript & @astrojs/check — check the code for mistakes while developing.
  • @types/node — type hints that make editing nicer; not shipped.
  • playwright — runs automated browser tests.
  • wrangler — uploads the finished site to Cloudflare. A deploy tool, not part of the site.
  • satori & @resvg/resvg-js — generate social-preview images at build time.

See the pattern? The dependencies are the ingredients of the website itself. The devDependencies are the kitchen tools — essential for cooking, but they don't go on the plate.

How you choose the list when installing

The install command decides the list for you — the -D flag is the whole difference:

Pick the right list

Terminal
$ npm install three        # → dependencies (project needs it)
$ npm install -D playwright  # → devDependencies (dev tool only)
In VS Code
  1. Run in the integrated terminal
  2. Open package.json afterwards to see which list the package landed in
  3. Got it wrong? Uninstall and reinstall with or without -D

Plain 'npm install <pkg>' goes to dependencies. Add -D (or --save-dev) to put it in devDependencies instead.

Does the difference actually matter?

For a site you build and deploy yourself, both lists install the same way when you run npm install locally, so it might feel academic. It starts to matter in two cases:

  • Lean production installs — running npm install --omit=dev skips devDependencies, for a smaller, faster install on a server that only needs to run the result.
  • Publishing a package — if you share your package, anyone installing it gets your dependencies but not your devDependencies. Putting a build-only tool in the wrong list would force it onto all your users.

Even when it doesn't change behaviour, keeping the split tidy makes package.json readable — anyone can see at a glance what the project truly relies on.

"Dependencies of dependencies"

You list only a handful of packages, yet node_modules holds hundreds. That's because each package has its own dependencies, which have theirs, and so on. These are called transitive dependencies — npm fetches the entire tree automatically so you never have to chase them down yourself.

You may also see peerDependencies in some packages. That's a package saying "I work alongside X, but you should install X yourself" — common for plugins that attach to a framework. As a beginner you rarely set these; just install whatever a warning tells you is missing.

Up next: those scripts in package.json — how npm run dev and npm run build actually work.

FAQ

Frequently Asked Questions

What is the difference between dependencies and devDependencies?
dependencies are packages your project needs to build or run (here: astro, tailwindcss, three). devDependencies are tools you only need while developing (here: typescript, playwright, wrangler).
How do I add a devDependency?
Use the -D flag: npm install -D <pkg> (long form --save-dev). Without it, npm install <pkg> adds to dependencies.
What are transitive dependencies?
The dependencies of your dependencies. You list a handful of packages, but each of those may pull in dozens more — which is why node_modules ends up so large.
Does the dependency vs devDependency split matter for a website?
For a site you build and deploy yourself, both groups install the same way locally. The split matters when a package is published, or when you run npm install --omit=dev to skip dev tools in production.