Ctrl + K
JavaScript14 min read

package-lock.json Explained

Understand how package-lock.json works, what information it stores, how npm uses it for reproducible installations and how to manage lockfiles safely.

Published: 2026-09-02

The package-lock.json file is an automatically generated lockfile used by npm to record the exact dependency tree installed for a JavaScript or Node.js project. While package.json describes which packages a project depends on, package-lock.json records the resolved versions and other information needed to reproduce that installation.

Understanding package-lock.json is important because it affects dependency installation, reproducible builds, continuous integration and dependency updates. Developers frequently encounter the file in Git repositories without knowing why it exists, whether it should be edited manually or what happens when it changes.

What Is package-lock.json?

package-lock.json is a lockfile generated by npm when dependencies are installed or changed. It describes the dependency tree that npm resolved for the project, including package versions, dependency relationships and integrity information.

The main purpose of the lockfile is reproducibility. A package.json dependency such as ^4.18.0 allows npm to install compatible versions within a specified range. The lockfile records the concrete version that was resolved, so another installation can use the same resolution when the lockfile is respected.

package.json vs package-lock.json

FilePurpose
package.jsonDescribes project metadata, scripts and declared dependencies
package-lock.jsonRecords the resolved dependency tree and exact package information

The two files work together rather than replacing each other. package.json expresses what the project requests, while package-lock.json records what npm resolved for that request.

package.json
    ↓
Dependency version ranges
    ↓
npm resolves dependencies
    ↓
package-lock.json
    ↓
Resolved dependency tree

Why Does package-lock.json Exist?

Without a lockfile, installing the same package.json at different times can potentially produce different dependency trees because dependency ranges may resolve to newer compatible versions. This can create differences between developer machines, CI environments and production systems.

  • Record resolved dependency versions.
  • Improve installation reproducibility.
  • Make dependency changes visible in Git.
  • Help CI systems install consistent dependency trees.
  • Record package integrity information.
  • Preserve information about transitive dependencies.

How package-lock.json Works

When npm installs dependencies, it reads package.json and resolves the project's dependency graph. npm then writes the resulting dependency information to package-lock.json. On later installations, npm can use the lockfile to reproduce the previously resolved tree when it remains compatible with package.json.

package.json
    ↓
npm install
    ↓
Resolve dependency graph
    ↓
Write package-lock.json
    ↓
Install dependencies
💡 Think of package.json as the project's dependency requirements and package-lock.json as a detailed record of the dependency tree npm resolved from those requirements.

What Information Does package-lock.json Store?

The exact structure depends on the npm and lockfile format being used, but modern package-lock.json files can contain information about the project, package locations, resolved versions, dependency relationships, registry URLs and integrity hashes.

InformationPurpose
lockfileVersionIdentifies the lockfile format
packagesContains information about installed package locations
versionRecords a resolved package version
resolvedIdentifies where a package was retrieved from
integrityProvides a package integrity hash
dependenciesDescribes dependency relationships in supported lockfile formats

The lockfileVersion Field

The lockfileVersion field identifies the lockfile format used by npm. Different npm generations introduced different lockfile formats, so this value helps npm understand how the file should be interpreted.

{
  "name": "example-project",
  "version": "1.0.0",
  "lockfileVersion": 3
}

You should generally let npm manage this value rather than changing it manually. Using a different npm version can cause a lockfile to be updated to another supported format.

The packages Section

Modern package-lock.json files commonly contain a packages object. Its entries describe package locations relative to the project, including the root project and installed dependencies.

{
  "packages": {
    "": {
      "name": "example-project",
      "version": "1.0.0",
      "dependencies": {
        "express": "^5.0.0"
      }
    },
    "node_modules/express": {
      "version": "5.0.0"
    }
  }
}

The empty string key represents the root project in this example. A path such as node_modules/express identifies the installed package location within the dependency tree.

The version Field

The version field records the concrete version of a package that was resolved. This is different from the version range that may appear in package.json.

FileExample
package.json"express": "^5.0.0"
package-lock.json"version": "5.0.0"

The caret in package.json represents a version range, while package-lock.json can record the specific version selected during dependency resolution.

The resolved Field

The resolved field can identify the location from which npm retrieved a package, commonly using a registry URL. This information helps describe the source associated with the resolved package.

"resolved": "https://registry.npmjs.org/example-package/-/example-package-1.2.3.tgz"

The integrity Field

The integrity field contains a cryptographic integrity value associated with a package. npm can use this information to verify that the package content corresponds to the expected artifact.

"integrity": "sha512-example-integrity-value"

Integrity information is one reason a lockfile contains more than just package names and versions. It records additional details that help npm reproduce and verify dependency installations.

Direct vs Transitive Dependencies

A direct dependency is a package explicitly declared by your project. A transitive dependency is a package required by one of your dependencies. package-lock.json records the resolved dependency tree, so it can contain many packages that are not listed directly in package.json.

Your application
    ↓
Package A
    ↓
Package B
    ↓
Package C

In this example, the application directly depends on Package A, while Packages B and C are transitive dependencies. Even though the application does not explicitly request B or C, their versions can affect the resulting installation.

Why Transitive Dependencies Matter

A project's dependency tree can contain hundreds or thousands of packages even when package.json lists only a small number of direct dependencies. A lockfile records the resolved tree so that these indirect dependencies are also represented.

⚠️ Do not assume that only the packages listed in package.json matter. Transitive dependencies can affect application behavior, installation results, build processes and security.

Should package-lock.json Be Committed to Git?

For most Node.js applications and other deployable projects, package-lock.json should be committed to version control. Keeping the lockfile in Git allows developers and CI systems to share the same resolved dependency information.

  • Commit package-lock.json for applications.
  • Review meaningful lockfile changes during dependency updates.
  • Keep the lockfile synchronized with package.json.
  • Use the lockfile in CI and production installations.
  • Avoid manually editing dependency entries.

When Should package-lock.json Change?

The lockfile normally changes when dependencies are added, removed or updated, or when npm changes the dependency resolution represented by the lockfile. It can also be rewritten when a different npm version updates the lockfile format or metadata.

ActionPossible Lockfile Result
npm install new-packageNew dependency information is added
npm uninstall packageDependency information is removed
npm updateResolved package versions may change
Change package.json dependencyRelated dependency tree may change
Change npm versionLockfile format or metadata may change

npm install vs npm ci

npm install and npm ci both work with package-lock.json, but they serve different purposes. npm install is commonly used during development when dependencies need to be added, updated or resolved. npm ci is designed for clean, reproducible installations and expects a lockfile that is consistent with package.json.

CommandTypical Purpose
npm installInstall or update dependencies during development
npm ciPerform a clean installation based on the lockfile

In continuous integration environments, npm ci is often preferred because it installs from the lockfile and starts with a clean dependency directory rather than treating the installation as an opportunity to update the dependency tree.

npm ci
💡 Use npm install when intentionally changing project dependencies and npm ci when you want CI or another clean environment to reproduce the dependency tree described by the lockfile.

What Happens If package.json and package-lock.json Differ?

The lockfile is intended to represent the dependency requirements in package.json. If package.json is changed manually without updating package-lock.json, the two files can become out of sync.

For example, changing a dependency version range in package.json without installing or updating the dependency can leave the lockfile describing a different dependency requirement. npm may then update the lockfile during installation or reject the installation in commands that require a synchronized lockfile.

⚠️ Do not manually edit package.json and assume the existing package-lock.json will automatically become correct. After changing dependencies, use npm to resolve and update the lockfile.

Can You Edit package-lock.json Manually?

Technically, package-lock.json is a JSON file and can be edited like other text files, but manually changing dependency entries is usually a bad practice. npm owns the lockfile and can regenerate or modify information during installation.

If you need to change a dependency, update package.json or use the appropriate npm command and let npm generate the corresponding lockfile changes.

Why Is package-lock.json So Large?

A package-lock.json file can become large because it describes the complete resolved dependency tree rather than only the direct dependencies declared by the project. Modern JavaScript applications can depend on a large number of transitive packages.

A large lockfile is therefore not necessarily a sign of a problem. Its size usually reflects the complexity of the dependency tree and the amount of metadata npm records for reproducible installations.

Why Does package-lock.json Change So Much?

A dependency update can affect many transitive packages. If a direct dependency changes, npm may resolve a different part of the dependency tree, causing multiple entries in package-lock.json to change even though only one package was intentionally updated.

Lockfile changes should therefore be reviewed rather than judged only by the number of changed lines. A large diff can be legitimate when a dependency has a large or deeply connected dependency tree.

How to Read a package-lock.json Diff

  • Identify which direct dependency triggered the change.
  • Check whether resolved versions changed.
  • Look for added or removed transitive dependencies.
  • Review changes to integrity values and package sources.
  • Check for unexpected major-version changes.
  • Confirm that the application and tests still work.

Do Not Delete the Lockfile to Fix Every Problem

Deleting package-lock.json and running npm install can sometimes resolve a corrupted or inconsistent dependency state, but it should not be the default solution for dependency problems. Regenerating the lockfile can produce a significantly different dependency tree.

A better approach is to understand why the installation fails, inspect the dependency conflict and update only the relevant packages when possible. If the lockfile genuinely needs to be regenerated, the resulting changes should be reviewed carefully.

package-lock.json and Security

Lockfiles play an important role in dependency security because they record the concrete dependency tree used by the project. This makes dependency changes easier to review and helps teams identify exactly which versions are being installed.

However, having a lockfile does not automatically make a project secure. Vulnerable packages can still be locked to vulnerable versions. Security requires regular dependency maintenance, vulnerability scanning and appropriate updates.

  • Review dependency updates.
  • Monitor known vulnerabilities.
  • Keep important dependencies maintained.
  • Inspect unexpected dependency additions.
  • Avoid untrusted packages.
  • Use automated security checks where appropriate.

package-lock.json in CI/CD

Continuous integration systems benefit from lockfiles because automated builds should use predictable dependency versions. A CI pipeline can install the dependency tree recorded by package-lock.json instead of resolving potentially different versions every time a build runs.

npm ci
npm test
npm run build

Using a clean installation in CI helps detect problems that may be hidden by dependencies left over from previous local installations. It also makes the build environment closer to a fresh production installation.

package-lock.json in Production

Production deployments should generally install the dependency versions that have been tested rather than resolving a new dependency tree during deployment. For npm-based applications, this commonly means committing package-lock.json and using a lockfile-based installation strategy in the deployment environment.

package-lock.json and Node.js Version

The lockfile controls dependency resolution, but it does not replace the need to standardize the Node.js runtime. Native modules and packages can depend on Node.js versions, operating-system capabilities or runtime-specific behavior.

💡 For reproducible builds, standardize both the Node.js runtime and the package manager. A lockfile alone cannot guarantee identical behavior across fundamentally different runtime environments.

Common package-lock.json Mistakes

  • Deleting the lockfile whenever npm reports an error.
  • Manually editing package-lock.json dependency entries.
  • Ignoring large lockfile changes without reviewing them.
  • Changing package.json without updating the lockfile.
  • Using different npm versions without understanding lockfile changes.
  • Ignoring transitive dependency changes.
  • Not committing the lockfile for an application project.
  • Using npm install in CI when a clean lockfile installation is more appropriate.

Best Practices

  • Commit package-lock.json to version control for application projects.
  • Let npm generate and maintain the lockfile.
  • Use npm install when intentionally changing dependencies.
  • Use npm ci for clean CI installations.
  • Review lockfile changes during dependency updates.
  • Keep package.json and package-lock.json synchronized.
  • Keep the Node.js and npm versions consistent across environments.
  • Use dependency security checks regularly.

Frequently Asked Questions

What is package-lock.json used for?

package-lock.json records the resolved dependency tree for an npm project, including package versions and other metadata needed to reproduce dependency installations.

Should I commit package-lock.json?

Yes. For most application projects, committing package-lock.json is recommended because it allows developers and CI systems to use a consistent resolved dependency tree.

What is the difference between package.json and package-lock.json?

package.json declares project dependencies and version ranges, while package-lock.json records the concrete dependency resolution and additional information about the installed dependency tree.

Can I delete package-lock.json?

You can, but deleting it causes npm to resolve dependencies again and can produce many dependency changes. It should not be the default solution for installation problems.

Can I edit package-lock.json manually?

Although it is possible to edit the JSON file, manual dependency changes are generally discouraged. Use npm commands or modify package.json and let npm update the lockfile.

Why is package-lock.json so large?

The lockfile records the complete resolved dependency tree, including transitive dependencies and package metadata. Large JavaScript projects can therefore have large lockfiles.

Why did package-lock.json change after npm install?

npm may update dependency resolutions, metadata or the lockfile format. Changes can also occur when package.json requirements, dependency versions or the npm version change.

What is the difference between npm install and npm ci?

npm install is commonly used for dependency changes and development installations, while npm ci performs a clean installation based on the existing lockfile and is commonly used in CI environments.

Helpful JavaScript Tools

A Package-lock Inspector helps examine package-lock.json contents and dependency information, a Package.json Formatter makes package.json files easier to read, a Package.json Validator checks package.json structure, an npm Dependency Checker helps inspect project dependencies, and a Semantic Version Comparator makes it easier to compare package versions and understand version ranges.

Conclusion

package-lock.json is an important part of npm-based JavaScript projects because it records the resolved dependency tree behind package.json. It helps make installations reproducible, exposes transitive dependencies, preserves package integrity information and provides a useful record of dependency changes.

For most application projects, the best approach is to commit package-lock.json, let npm manage it, review meaningful changes and use lockfile-based installations in CI. Understanding how package-lock.json works makes dependency management easier and helps prevent unnecessary lockfile regeneration, inconsistent environments and unexpected dependency changes.

Found an issue?

Found an error, outdated information, or something missing from this article? Let me know through the Contact page.

Your feedback helps improve our articles and keep them accurate and useful.