Peer dependencies — NPM

Thabo Ambrose
5 min readJun 24, 2019
NPM

A quick look into what are peerDependencies, how to use them and when.

In this article, I am hoping to understand what is the peerDependencies entry in a package.json, why these kind of dependencies are critical for some cases and how to use them in projects.

The package.json

A project with a file named package.json in the root of the project’s directory is considered a NPM project, meaning that the project can use other people’s code/packages published to the NPM repository. A package.json is a file defining some facts about a project and some settings on how the project will behave when interacted/interfaced with, in addition to the facts and behavior, a package.json file defines the dependencies and their versions the project depends on.

The package.json file is a file written in a JSON format, an example of this file is as follows:

As you can see we have entries of the name of the project, the version, the author .etc. I will not delve deep into what each entry in this file is for as there are quite a few that are not even present in the image above. For a complete list of all the possible entries, their possible values and descriptions can be found in the link here.

For this article, I will focus on the dependencies, devDependencies and peerDependencies properties of the package.json object. Let’s explain these three entries like they did in the official documentation.

Dependencies- Dependencies are specified in a simple object that maps a package name to a version range. The version range is a string which has one or more space-separated descriptors. Dependencies can also be identified with a tarball or git URL.

devDependencies- If someone is planning on downloading and using your module in their program, then they probably don’t want or need to download and build the external test or documentation framework that you use.

In this case, it’s best to map these additional items in a devDependencies object.

peerDependencies- In some cases, you want to express the compatibility of your package with a host tool or library, while not necessarily doing a require of this host. This is usually referred to as a plugin. Notably, your module may be exposing a specific interface, expected and specified by the host documentation.

optionalDependencies- If a dependency can be used, but you would like npm to proceed if it cannot be found or fails to install, then you may put it in the optionalDependencies object.

bundledDependencies- This defines an array of package names that will be bundled when publishing the package.

Source

In cases where you need to preserve npm packages locally or have them available through a single file download, you can bundle the packages in a tarball file by specifying the package names in the bundledDependencies array and executing npm pack.

So what is a peerDependency? It is a dependency that could be required by the consuming project/library or other third-party packages/libraries consumed by the project that is depending on your project/library and also is required by your project/library which is the one defining the peerDependency. If that makes no sense, maybe an example would be better, take a look at the image below

Let’s suppose that the above project is a library with the name my-demo and it is a tooltip component, my-demo depends on tippy.js, webpack and @angular/core, and note that these are 3 different types of dependencies in the package.json, but the focus is on @angular/core as that is our peerDependencies.

Now since my-demo is a library, we know that some other project(let’s call it deeApp) will be consuming our library and that consuming project is an Angular 7 project.

Now since we know that the consuming project(deeApp) is an Angular project and is dependent on @angular/core, and specifically v7, we are going to have a conflict when installing my-demo in the consuming project(deeApp) when resolving the @angular/core package, why? because our consuming project depends on the version 7 (@angular/core@7.0.0) of the peerDependency instead of the one specified in our library which is (@angular/core@8.0.2). So NPM will check to see if the peerDependency exists in deeApp if it does not exist it will install the package version specified in our library’s peerDependency in the deeApp project, not in my-demo.

It is a bad idea to expose third-party libraries in a plugin/library project.

If NPM finds that the peerDependencies is present in the consuming project (deeApp), that is when you will get a warning that there is an unmet peer dependency. Now you will have to fix this manually, or maybe there is an NPM command to fix this already that I do know of.

Remember that a peerDependency is added manually in the package.json file. You can leverage the Semver to specify the peerDependenc’s versions that will meet your needs but the starting point would be to follow from what the consuming project is using already, so for library authors you would have to satisfy consumers of your library.

So now we know the implications of the peerDependencies, they alleviate, or help circumvent the potential issues that come with version conflicts from packages.

When should I use peerDependencies?

  • When you are building a third party library/plugin
  • When the consumers of your library are using a dependency your library is also depending on .eg (@angular/core — Our my-demo library depends on @angular/core to have the decorators like @Component).
  • Your library/plugin exposes a dependency interface, for example, an Angular component library uses the @Component, @Injectable or @Directive decorators, these functions are objects from the @angular/core package so having conflicting versions in the library and the consuming project of the library might cause some runtime errors because of maybe the changes in the API, signature or implementation of the decorator functions if for example @Component object/function in v8 expects a required parameter that was not required in v7.

There are other cases and edge-case that need to be considered when working with the peerDependencies, there is no specific approach on the usage albeit there are common use cases.

Taking from a blog from the nodejs.org, check the image below

Some helpful links
ST, lexy-lambda, nodejs.org, some source , NPM-Semver

And you can close the gap by reading a cool blog related to all this here, thanks to the dude Manuel by the way.

Cheers ✌

--

--