A lockfile is the record of exactly which version of every package actually got installed, down to the deepest transitive dependency, at the moment someone ran an install. Your manifest, such as package.json or its equivalent, usually specifies a range, not an exact version, which leaves room for a package manager to pick differently on two different days. The lockfile removes that ambiguity: it is the precise, complete list that makes an install reproducible instead of a moving target.
What changed in 2026
- Lockfile formats got faster to parse and smaller to diff, with newer formats like
pnpm-lock.yaml designed to produce cleaner, more reviewable pull request diffs than earlier generations.
- Registries added provenance data that lockfiles now reference, tying a locked version to a verifiable build origin, which strengthens supply chain checks that read the lockfile as their source of truth.
- Strict, lockfile-only install commands became the enforced default in CI templates, catching manifest and lockfile drift automatically instead of relying on someone noticing.
- Automated dependency update tools got better at producing clean, minimal lockfile diffs, reducing the noisy, hard-to-review lockfile changes that used to accompany routine updates.
Why a manifest alone is not enough
A typical manifest entry looks like "lodash": "^4.17.21", a range, not a single version. That range is a deliberate courtesy: it lets you receive safe patch and minor updates without editing the manifest yourself. But it also means the install command has a decision to make every time it runs, and "the highest version matching this range" can be a different actual version depending on what has been published since the last install.
A lockfile removes that decision entirely for anyone installing from it. It lists the exact resolved version of every package, including packages you never listed directly, so a fresh install produces the identical dependency tree every time.
What is actually inside one
| Field it typically records |
Why it matters |
| Exact resolved version |
The specific version actually installed, not just the range that allowed it |
| Resolved download location |
Where the package was fetched from, for integrity and reproducibility |
| Integrity hash |
Confirms the downloaded file matches what was locked, catching tampering or corruption |
| Full dependency graph |
Every transitive dependency too, not just what you declared directly |
Committing it, and installing from it correctly
Always commit the lockfile alongside the manifest. Together they are the two files that let a teammate, a CI runner, or a production build reproduce your exact dependency tree.
In CI, use the strict install mode your package manager provides, such as npm ci or pnpm install --frozen-lockfile, rather than a plain install. The strict mode refuses to modify the lockfile and fails loudly if the manifest and lockfile disagree, instead of silently generating a slightly different tree than what you tested against locally.
-- CI: install exactly what the lockfile specifies, fail if it is out of sync
npm ci
-- local development: allowed to update the lockfile when the manifest changes
npm install
Handling lockfile conflicts
Two branches that both add or update dependencies will often conflict in the lockfile during a merge, since both touched the same region of a large generated file. The reliable fix is to resolve the manifest conflict first, then delete and regenerate the lockfile with the package manager rather than hand-editing the merge conflict markers. A manually patched lockfile can describe a dependency tree that was never actually installed or tested.
FAQ
Should a lockfile ever be edited by hand?
No. It is a generated file, and hand-editing risks a lockfile that does not match any real, tested install. Change the manifest and let the package manager regenerate the lockfile instead.
What is the difference between npm install and npm ci?
npm install will update the lockfile if the manifest allows a different resolution. npm ci installs strictly from the existing lockfile and fails if it does not match the manifest, which is the right choice for CI and production builds.
Does every package manager use the same lockfile format?
No. npm uses package-lock.json, pnpm uses pnpm-lock.yaml, and Yarn has its own yarn.lock. They are not interchangeable, which is one reason mixing package managers in one project causes problems; see npm vs yarn vs pnpm for how the tools otherwise differ.
Why did my install produce a different version than a teammate got last week?
Almost always a missing or outdated lockfile, or a range in the manifest that was wide enough for a new package version to satisfy it differently between installs. Committing and installing strictly from the lockfile is exactly what prevents this.
Where to go next