Skip to content

Intro to NPM

NPM stands for “Node Package Manager”. It's a command-line tool that allows developers to download and manage dependencies. It comes bundled with Node.js, so you don't need to install anything. You get it for free by installing Node.js.

The term "NPM" can also refer to the NPM repository, a library of over a million public JavaScript packages.

When a new version of React is released, the React developers publish it to the NPM repository. Then, developers like you and me can install or update our projects using the NPM command-line tool.

Let's talk about how to use the NPM command line tool.

Manifest files

Every project that uses NPM will have a central manifest file called package.json. Here's a quick example:

{
"name": "my-project",
"scripts": {
"dev": "parcel public/index.html",
"build": "parcel build public/index.html",
},
"dependencies": {
"react": "18.2.0",
"react-dom": "18.2.0",
}
}

The relevant thing here is the dependencies object. This file tracks which dependencies we've downloaded from the NPM repository. In this example, we've downloaded the react package version 18.2.0, as well as react-dom version 18.2.0.

We can create a new package.json file by running the following command:

npm init

You'll be asked some questions. The answers don't really matter, and can easily be changed later. You can skip the questions by calling with a flag: npm init -y.

Depending on your tooling setup, you may or may not have to do this. With Parcel, we do need to provide our own package.json. With Next.js (discussed in Module 6), this file is created for us automatically.

Installing dependencies

Once you have a package.json file, you can add a new dependency with the npm install command:

npm install some-package-name

When you run this command, the npm command-line tool will search the NPM repository for a package called some-package-name. If it exists, the latest version of that package will be downloaded.

Unlike applications on your computer, NPM dependencies are project-specific. They aren't installed globally, they're downloaded into a local directory within the project called node_modules. In other words, dependencies aren't shared between projects. If you have 5 projects that all use React, you'll have 5 copies of the React dependency on your machine.

In addition to downloading the package, this command will also update the project's package.json file, so that this dependency is tracked.

You can install multiple dependencies at once by space-separating them. For example, with React, we need two distinct packages (react and react-dom), so we can do it like this:

npm install react react-dom

Reinstalling dependencies

Every now and then, things get kinda funky.

If you're experiencing weird bugs that don't make any sense, it can be helpful to blow away all of the third-party code and re-install it. This is the JavaScript version of "did you try turning it off and on again?".

Here's the good news: because we're storing information about the dependencies in package.json, we can always fetch fresh copies from the NPM repository.

First, delete the node_modules directory from the command line:

rm -rf node_modules

Then, reinstall them with the following command:

npm install

The npm command-line tool will see that there are a bunch of dependencies listed in the package.json manifest, and that none of them are actually available. And so it'll create a new node_modules directory and download fresh copies of every listed dependency into it.

Lockfiles

As you install dependencies, a new file will be created/updated: package-lock.json.

This is known as a lockfile. It will “lock” all of the dependencies in a project to a specific version.

This is a really important thing when working in a team. It guarantees that every developer on the team will have the exact same code in their node_modules folder. It produces a more-consistent experience between developers.

This might seem a bit strange… after all, aren't we already tracking the versions we use in our package.json file? Why do we need a second file to track this??

The lockfile is necessary for two reasons:

  1. The versions in package.json often have some “wiggle room”. They aren't always exact matches to a specific version (eg. 18.1.2). In general, they match against major versions (eg. 18.x).
  2. Our package.json only lists the direct dependencies used by our project, not any sub-dependencies.

For example, when you install react and react-dom, you'll actually wind up downloading several other NPM packages, including:

  • js-tokens
  • loose-envify
  • object-assign
  • scheduler

These packages are used internally by react and react-dom.

This can go many levels deep. For example, that js-tokens package has its own set of ~10 dependencies. You might only install 5 or 10 top-level dependencies, and wind up with hundreds of packages in your node_modules folder. package.json doesn't track sub-dependencies at all.

If you use a version control system like Git, you'll want to make sure that the lockfile is tracked. That way, when another developer installs the project on their local machine, it'll include the lockfile, and they'll wind up with the exact same dependencies.