Understanding Dev, Peer, and Regular Dependencies

Tyler Hawkins - Sep 27 '21 - - Dev Community

When building a modern JavaScript app, you will likely rely on many other packages and third-party dependencies. For example, you might use react and react-dom for your UI, lodash for utility helper methods, webpack or rollup as your bundler, and jest with @testing-library/react for unit tests.

All of these packages are stored as dependencies in your package.json file. Understanding whether to include them as dependencies, devDependencies, or peerDependencies can sometimes be a struggle for newer developers.

In this article we'll look at the three types of dependencies found in the package.json file and what each does.


Dependencies

dependencies are packages used in your app's production bundle. If you're building a React app, then react and react-dom would be dependencies. If you're using react-router for client-side routing, that would also be part of your dependencies. Any other packages like lodash or a design system library like Material UI (@mui/material) would also be dependencies.

There is an exception to this rule, but we'll cover this down in the Peer Dependencies section below.


Dev Dependencies

devDependencies are typically the tooling you use to build your project, but they're not actually included in the app's production bundle. For example, webpack and rollup would both be devDependencies. These are bundlers used to compile your app, but they're not part of your app.

The same goes for test frameworks and libraries like jest, @testing-library/react, karma, mocha, or jasmine. These libraries are needed for writing unit tests for your code, but they're not part of your production app.

Linters and code formatters like eslint, prettier, commitlint, husky, and lint-staged would all be devDependencies as well.


Peer Dependencies

peerDependencies are dependencies that your app relies on but expects another package to provide. peerDependencies are a crucial tool in reducing the size of your final production app. Let's take a look at why.

Imagine that you have a large React app composed of many npm packages. Let's say there are 20 of them and they all use React. If each of these packages lists react and react-dom in their dependencies section, then that means you'll have 20 copies of React in your app! (21 if you count the copy that your main parent app provides as well.)

In order to avoid this massive bloat of dependencies, library developers can instead use the peerDependencies section to specify that their library depends on react and react-dom, but the consumer of the package needs to be the one to provide it. In doing so, you can then have your app with 20 dependencies but only have one copy of react and react-dom used in production provided in the dependencies in the package.json file of your parent app.

When using peerDependencies, it's important to note that if you mark a package as a peer dependency, you also must include it in your devDependencies section. This allows your app to install and use packages like react and react-dom during local development and unit testing, but it won't include them as real dependencies in the production build.

Finally, when using a peer/dev dependency combination, you also should add the package in the externals configuration section of your Webpack or Rollup config file.


Conclusion

That's it! Now you know how to properly work with dev, peer, and regular dependencies in any JavaScript project. Doing so will help you reduce the final bundle size of your app and ensure that only the necessary packages are installed in production.

Thanks for reading!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .