Skip to main content

What is a package?

From npm's perspective:

A package is a file or directory that is described by a package.json file.

Most commonly you'll be working with a directory-based package and package.json, also referred to as the "package file", will be at the root of that directory. For example, if you bootstrap a package with Create React App, the directory contents look like this:

$ tree -L 1 my-react-app
my-react-app
├── README.md
├── node_modules
├── package-lock.json
├── package.json
├── public
└── src

Package file

The package file contains metadata describing the package and its dependencies. The package file for the above CRA-based package is:

package.json
{
"name": "my-react-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
// ...
}

We'll cover many of these properties throughout these docs, but for now note the package is named my-react-app.

Lock file

As well as the package file there is usually a "lock file", package-lock.json1. Where the package file includes only direct dependencies and generally has ranges for their versions, the lock file includes the whole dependency tree with explicit versions for every package. For the above package file, the lock file would be:

package-lock.json
{
"name": "my-react-app",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "my-react-app",
"version": "0.1.0",
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
// ...
}
}
}

The packages object contains information about all of the packages in the dependency tree, including the base package (which has the special key "" in the packages object), and their dependencies.

The lock file is almost always substantially larger than the package file, because it has to contain a lot of explicit information about the whole dependency tree (in the above example package-lock.json is 675kb, covering over 1,500 packages, whereas package.json is 0.8kb).

v3 lock files are compatible with npm v7 and above. If you have a pre-v3 lock file, you can upgrade it to v3 (and halve the storage space compared to v2 lock files, which effectively contain v1 and v3) with:

$ npm install --lockfile-version=3 --package-lock-only
tip

Don't add package-lock.json to your .gitignore. Keeping a consistent dependency tree is important, especially if you don't want to deal with hard-to-debug issues between different environments, and package.json alone does not contain enough information to ensure this.

Scoped packages

A scoped package is way of grouping related packages. The scope is usually shown as part of the package name: @{scope}/{name}.

For example, the Angular framework's packages are all published to the public npm registry under the angular scope:

  • @angular/cli;
  • @angular/core;
  • @angular/router;
  • ... etc.

This can be slightly confusing because packages are often shown as {name}@{version}, so when there's a scope involved you'll see multiple @ signs: @{scope}/{name}@{version} (e.g. @angular/[email protected]).

Footnotes

  1. There is an alternative, npm-shrinkwrap.json, but here we'll focus mainly on the package-lock.json.