An Introduction to the 3 Types of Dependencies in React Libraries

Introduction to project dependencies, devDependencies, and peerDependencies.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the Creating React Libraries from Scratch course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Creating React Libraries from Scratch, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Creating React Libraries from Scratch

The end goal of writing a library is having an application consumer code. In other words, our library would become a dependency in another project. In the same way as a project can depend on our library, our library can also have its own dependencies. In NPM there are three types of dependencies. There are dependencies we directly rely on called dependencies. Dependencies we only need for development called dead dependencies. And dependencies we expect users to provide for us, called pure dependencies. Our package.json file defines the three types of dependencies. We can use every type of dependency or none of them. There are no rules to the number of dependencies on a project. Let's take a look at an example package.json. When defining dependencies we want to stick to a version or a range of versions we know will work with our library. Packages in NPM use semantic versioning. The semantic versioning standard communicates changes to a project. These version numbers are immutable or cannot change once they're published. Semantic versioning is defined as major.minor.patch. When the major number is bumped there's a high probability of a break and change. For example, upgrading the react from 15.0.0 to 16.0.0 is a major jump in features and deprecations. The consuming code is likely to break. Minor numbers are incremental changes. These could be new features but break and changes are minimal. Lastly, patch numbers communicate bug fixes. There should be no break and changes. Now that version is cleared up, let's look at those symbols in front of the numbers. NPM has additional utilities to make handling dependencies easier. For example, the caret for webpack tells NPM to use version 5.0.0, except if a newer minor or patch version exists. If version 5.0.1 had been published when we ran Yarn, then version 5.0.1 would be installed instead of 5.0.0. These utility symbols are useful to reduce micromanaging dependencies, but gain a lot more power when used as peer dependencies. A full list of symbols can be found on the NPM documentation. Keep semantic version in mind when managing dependencies and publishing your own packages. Now let's dig a little bit deeper into each of these dependency types. Let's start with dependencies. The dependencies field in your package.json file defines the direct dependencies your project uses. These get bundled into your library with the code you wrote. Any code needed in production should be included in your dependencies. Development dependencies or dev dependencies are the tools and packages we use to develop our library. These don't get bundled into the final production build, so any dependencies needed for local development or testing long here. Examples of dev dependencies include webpack, storybook, and testing frameworks . Both items and dependencies and dev dependencies get downloaded to your local machine when running Yarn. Peer dependencies are dependencies that don't get bundled into a production build, but that we expect our users to include on their end. When running Yarn, peer dependencies don't get installed on your local machine. So why are peer dependencies necessary? They allow us to reduce conflicts in the bundle size of the code, which uses our library. For example, if scroller includes a React as a dependency, and then the application uses scroller, which has its own version of React, there would be two separate versions of React being initialized in the consumer application. This means we are including React twice, making users way longer for applications to load. Having fundamental libraries like React and React down as peer dependencies make a lot of sense for a React library. For scroller, we'll be using Yarn as a package manager, but there are many great ones out there like mpm and pmpm, etc. By default, Yarn installs dependencies as project dependencies. We want the dependency to be a dev dependency or peer dependency that we must include additional arguments. So if we were to install React into scroller, we would run Yarn add React, and this would add React as a dependency. If we wanted React to be a dev dependency, we would use --dev or just - upperc ase D. Then to install as peer dependencies, we would use --peer or - uppercase P. We can also install dependencies by updating our package adjation manually. Anytime the package adjation gets updated, run Yarn to pull in the latest changes and new dependencies. In this case, if we made Webpack 5.0.1, we could then run Yarn and pull in to quickly that package.