package-lock.json: The Complete Guide
What is package-lock.json? And why should you care?
What is package.json?
package.json is a versioning file that primarily contains the list of dependencies (libraries) your node.js project needs to run.
It also includes other meta information like scripts, author & license information, description, project properties etc.
An Inherent Problem
In the above
package.json, you can see that the
“dependencies” object maps package-name to the version range.
Dependencies and/or peer dependencies generally have a version range specified in the package.json file, not the exact version range.
npm install non-deterministic. So, when you run
npm install today, and then you run it again after 3 months, you may not end up with the same
Moreover, if another developer clones your project, and runs
npm install on it a few days later, they may have a different node_modules dependency tree. When multiple developers are working on the same repository (which is most likely the case in every organization), this might pose a big problem and lead to inconsistencies in the dependencies installed, or worse, breaking changes.
So, what’s the solution? First, let’s understand what the version range signifies. The version range is a string that contains one or more space-separated numbers. These numbers also contain some special symbols like
^ ~ < ||, e.g.
^1.0.4, ~2.3, 4.4.x, >=2.3.4, <1.0.9 ||
These symbols tell
npm different things:
Let's say I want to install a package “foo”. After I run
npm i foo, my package.json file would mostly have an entry like this:
Here, foo is installed with version
2.3.0 [major minor patch]. The caret symbol tells something more:
^2.3.0 — [Caret Symbol] This tells npm to upgrade to minor and patch versions, but not major versions. So, basically
2.3.4, 2.3.9, 2.4.5, 2.8 but not
3.0.0 onwards. (Upgrade to minor and patch, but not major)
~2.3.0 — [Tilde Symbol] This tells npm to upgrade to patch versions, but not minor and major versions. So
2.3.4, 2.3.9 but not
2.4.0 onwards. (Upgrade to patch, but not minor and major)
There are a lot of other symbols that denote different npm version updating strategies. The official npm website is a good reference.
"foo": "^2.3.0", running
npm install a few days later might automatically upgrade the minor/patch version. This is undesirable…
But don't worry, we have package-lock.json to the rescue…
What is package-lock.json?
package-lock.json is a lockfile that contains information about the dependencies/packages with their exact version numbers (*important) that were installed for a node.js project.
- It helps different developers working on the same repo to install the exact package versions installed previously, even if the packages have released new versions. This ensures the same
node_modulestree across different machines/environments.
package-lock.jsonfile is essentially used to lock dependencies to a specific version number.
- This file is automatically generated (or re-generated) when there is a change in either the
- Whenever we clone a repo and run
npm ion a new machine, npm will first look to see if a
package-lock.jsonfile is present. If yes, it will proceed by installing the packages given in that file. Otherwise, it will look into the
package.jsonfile and start installing the required dependency packages. (📦 A caveat to this is explained later in the article)
Should you commit your package-lock.json?
Yes, this file should be committed to the source repository so that when developers clone your repo, they can install dependencies that exactly match the ones installed on your machine/environment. This is basically to replicate node.js environments as it is on different machines.
npm v7, the lockfile includes enough information about the entire package tree thus reducing the need to read
package.jsonfiles, and thereby increasing performance as well
Why / When does npm install rewrite package-lock.json?
- 📦 Caveat:
package-lock.jsononly if the package(s) to be installed are within the version range of
- If the package version given in the lockfile is not in the version range of the
package.jsonfile, packages are updated &
- If you want the installation to fail instead of overwriting
You declare a dependency in package.json like:
Then you do,
npm install which will generate a package-lock.json with:
Few days later, a newer minor version of “foo” is released, say “2.4.0”, then this happens:
npm install — package-lock version is within the range (i.e. ^2.3.0) so 2.3.0 is installed
npm ci — This anyway only looks at the
package-lock.json so 2.3.0 is installed
Next, you manually update your package.json to:
npm install — package-lock version is not within the range (i.e. ^2.4.0) so 2.4.0 is installed and the package-lock.json is re-written to now show:
npm ci — This anyway only looks at the package-lock.json, but since the version is not within the range, it throws an error.
npm ci command is similar to npm install, except it’s meant to be used in automated environments such as test platforms, continuous integration, and deployment — or any situation where you want to make sure you’re doing a clean installation of your dependencies. (Source: npm docs)
So in a nutshell:
npm installis not deterministic, which poses a problem when you’re working on a repo (with multiple devs) containing thousands of dependencies.
package-lock.jsonfile ensures that the same node_modules tree is generated every time
npm installis run.
- A newer command
npm ciensures that it ALWAYS creates the same node_modules tree, otherwise throws an error.