Original package.json
Changed package.json

Package.json Diff: Compare npm Manifests Online

Paste the old package.json on the left, the new one on the right, and see exactly which dependencies, scripts, and engines changed. Runs in your browser, nothing uploaded.

What this package.json diff tool is

A free, in-browser tool for comparing two npm package.json files side by side. Paste the old manifest on the left, the new one on the right, and the differences highlight character by character. The text never leaves your machine, which matters when the manifest belongs to a private repo or an unreleased product.

It is built for the moment a Renovate or Dependabot PR lands and you need to know what actually changed beyond the version bumps. Did anyone slip a script edit in? Did the engines field move from Node 18 to Node 20? Did a peerDependency get tightened? GitHub's diff view answers part of this, but pasting two manifests into a clean two-pane view is faster when you are scanning multiple PRs in a row or comparing branches that are not yet pushed.

Underneath, this is the same JSON-aware diff that powers our compare-json page, with framing and copy tuned for npm and yarn workflows. Both manifests are parsed as JSON and pretty-printed before the diff runs, so cosmetic whitespace differences do not pollute the highlight. If you need raw text diffing for a non-JSON manifest format, our compare-text tool covers that, and compare-yaml handles pnpm-lock.yaml.

How the diff actually works

Each pane is parsed as JSON. If parsing succeeds, the manifest is reformatted with consistent two-space indentation and key order is preserved as written. If parsing fails (a stray comma, a missing brace, a copy-paste that grabbed too few characters) the tool falls back to plain text mode and tells you which line broke. Once both sides are valid JSON, the diff runs character by character, then a semantic cleanup pass groups changes back into readable chunks so a version bump from ^18.2.0 to ^19.0.0 reads as one edit, not nine character edits.

Insertions on the right pane render in green; deletions on the left pane render in red. The two panes scroll-lock together, so when you find a change deep inside devDependencies on one side, the other side jumps to the same key. Because the diff is text-level after pretty-printing, it sees structural changes the way a human reads them: a removed dependency disappears as a line, a changed semver range highlights inside the value, a new script slots into the scripts block at the position you placed it.

What the tool deliberately does not do: it is not a dependency resolver. It will not tell you which transitive packages a caret-range bump pulls in, whether a peer is satisfied, or whether the new version introduces a security advisory. For that, run npm install followed by npm audit locally, or use npm with a lockfile-aware service like Snyk or GitHub's Dependabot alerts. This page tells you what the manifest text says. The resolution work belongs to your package manager.

How to diff a package.json in three steps

Two text panes, one diff. No CLI flags, no install step, no patch format to read.

  1. 1

    Paste the old manifest on the left

    Copy the previous version of package.json from your editor, from git show main:package.json, or from a teammate. Paste into the left pane. The tool will parse it as JSON and pretty-print it; if the JSON is invalid, the editor will surface the parse error so you can fix the snippet before diffing.

  2. 2

    Paste the new manifest on the right

    Do the same with the updated version. If you are reviewing a Renovate or Dependabot PR, the easiest source is the raw file from the PR branch. Both files get pretty-printed with consistent indentation, so cosmetic whitespace edits will not show up as fake changes in the diff.

  3. 3

    Read the highlighted differences

    Deletions show as red strikethroughs on the left; insertions show as green on the right. Scan the dependency blocks first, then check the scripts section for command edits, then the engines and peerDependencies fields. Version bumps inside a single value highlight the changed characters so you can tell a patch bump from a major one at a glance.

When a package.json diff is the right call

Reviewing a Renovate or Dependabot PR

A bot opens fifteen PRs in a morning. Most are routine, but one slipped in a script change, or a tightened peerDependency, or an engines bump that breaks your CI image. The PR title says "chore(deps): bump foo from 4.1.0 to 4.2.1" and you trust it on autopilot. Paste the before-and-after package.json into the diff and you can verify in five seconds that nothing else moved. This is the single most common reason JS engineers reach for a manifest diff.

Comparing two branches before merging

You and a colleague both touched package.json on separate feature branches. Git will merge cleanly because the edits are in different blocks, but a clean merge does not mean a correct one. Paste the two branches' manifests into the diff to spot a script that one branch dropped, a dependency one branch downgraded, or a clash where both branches added the same package at different versions. Cheaper than discovering it after CI runs npm ci against the merged result.

Auditing what npm install promoted in package-lock.json

Editing package.json is half the change. The other half is what package-lock.json records about the resolved tree. After running npm install, paste the old lockfile next to the new lockfile to see which transitive packages came along for the ride. Surprise additions are common when a caret range pulled in a new minor of a deeply-nested dependency. For raw lockfile inspection our compare-json page handles the bigger files better; this page is best for the manifest itself.

Checking a downgrade after a regression

A release ships, a bug surfaces, you bisect, and the suspect is somewhere in the dependency tree. Paste the package.json from the last good release next to the current one. Strip out unchanged blocks mentally and focus on the highlighted version ranges. The fix is often a downgrade of one library to the version pinned in the last green build. Once you find the suspect, lock it with an exact version (no caret, no tilde) until the upstream issue is resolved.

Comparing your local manifest against a teammate's

Two engineers, one tricky merge, two different package.json files at the end of the rebase. The lockfile is even more confused. Paste both manifests side by side to see which keys disagree, then resolve them deliberately rather than accepting the result of git merge -X theirs without reading. This is also the right tool when onboarding a new contributor whose npm install picks up a different tree than yours and you suspect a manifest drift.

Pre-publish review of a library's package.json

Before you run npm publish on a library, the manifest fields that matter are different from an app: main, module, types, exports, files, peerDependencies, engines. Diff the about-to-publish manifest against the last published one. A dropped peerDependencies entry, a changed exports condition, or a tightened engines range can break consumers in ways that npm itself will not warn you about. The Node.js packages docs are the reference for what those fields mean.

Package.json fields worth knowing

The fields that show up in real package.json diffs and what they mean. Worth scanning before you sign off on a Renovate PR or merge two branches that both touched the manifest.

TopicWhat this tool does
dependencies vs devDependencies vs peerDependenciesdependencies ship with your package and are installed by anyone who consumes it. devDependencies are only installed when you run npm install in the project root, never for downstream consumers. peerDependencies are packages your library expects the host to provide (typically React, the test framework, the bundler) and npm 7+ will install them automatically unless they conflict. Moving a package between these blocks changes who pays the install cost.
Caret (^) vs tilde (~) semver rangesCaret ^1.2.3 allows any version up to but not including 2.0.0; this is npm's default and the loosest common range. Tilde ~1.2.3 allows patches only, up to but not including 1.3.0. 1.2.x is the same as tilde. * means any version (do not use this in production). latest resolves at install time and produces non-reproducible builds, so avoid it.
Exact pinning (no range prefix)Writing "react": "18.2.0" with no caret or tilde pins to that exact version. Combined with a lockfile, this gives you the most reproducible install at the cost of never receiving security patches automatically. Some teams pin everything; others rely on the caret plus lockfile combination. There is no universally right answer, but the trade-off is more deterministic builds versus more out-of-date dependencies.
package-lock.json determinismpackage-lock.json records the exact resolved version of every package in the dependency tree, including transitives. npm ci installs from the lockfile and refuses to modify it; npm install may update it if a range allows a newer version. For CI, always use npm ci. For developers, npm install is fine as long as you commit the resulting lockfile change.
workspaces field for monoreposThe workspaces array tells npm to treat sibling directories as linked packages in a monorepo. npm install at the root installs all workspaces and creates a single hoisted node_modules tree. Yarn and pnpm support workspaces with their own conventions, and pnpm in particular hoists less aggressively, which catches more dependency-leak bugs at install time.
engines fieldengines.node declares the Node.js versions your package supports. By default npm only warns when the host violates this; engine-strict=true in .npmrc turns it into a hard error. Bumping the engines field is a breaking change for consumers stuck on older Node, and it is the kind of change that hides inside a Renovate "chore" PR. Always read the engines diff.
exports field for ES modulesThe exports field controls which paths consumers can import from your package and which conditions (import, require, types, node, browser) resolve to which files. Adding or removing an entry is a breaking change for downstream code. The Node.js packages documentation covers the resolution rules in detail; treat any exports diff as a major-version-worthy edit unless you are deliberately adding a new entry point.
File ordering inside package.jsonThere is no enforced order for top-level keys in package.json. By convention, most projects start with name, version, description, then scripts, then dependencies. Within dependency blocks, alphabetical order by key is the de facto standard and most package managers will sort the block on save. A diff that shows reordered but otherwise identical entries is usually a tooling difference, not a real change.

Package.json diff: frequently asked questions

How is this different from npm diff or npm-check-updates?

npm diff is a built-in npm command that compares two published versions of a package on the registry, including their tarballs and sources. npm-check-updates (ncu) reports which dependencies in your manifest have newer versions available. Neither shows you the difference between two arbitrary package.json files you have on disk or in two branches. This tool does that. Use ncu to find out you should upgrade, npm diff to see registry-side changes between releases, and this page to read your own before-and-after manifest in a side-by-side view.

Does it audit security or check for known vulnerabilities?

No. This page only diffs the text. It does not query the npm registry, the GitHub Advisory Database, or any vulnerability service. For security auditing, run npm audit against an installed tree, use npm audit signatures to verify package provenance, or rely on Snyk, Socket, or Dependabot alerts. This tool is the right call when you want to know what changed in the manifest. It is the wrong call when you want to know whether the change is safe to ship.

Does it detect transitive dependency changes?

Not from the manifest alone. package.json only lists direct dependencies and their requested ranges. The full resolved tree, including transitives, lives in package-lock.json (or yarn.lock or pnpm-lock.yaml). To compare resolved trees, paste both lockfiles into a diff. Because lockfiles are large, our compare-json page handles them better than this one. For pnpm-lock.yaml specifically, use compare-yaml. This page is optimised for the manifest.

How do I compare two package-lock.json files?

Paste both lockfiles into the panes the same way you would a manifest. The tool will parse them as JSON, pretty-print, and diff. Be aware that lockfiles can run to thousands of lines, so the highlighted diff may be long. Focus on the top-level packages entries first, then the version fields. For files larger than roughly five thousand lines, our compare-json page is a better fit because it is set up to handle big JSON payloads with the same engine.

What is the difference between caret (^) and tilde (~) ranges?

Both are semver ranges that allow updates without manually editing the manifest. The caret ^1.2.3 allows any version that does not change the leftmost non-zero digit, so 1.2.3 through 1.999.999 are accepted, but 2.0.0 is not. The tilde ~1.2.3 is stricter: it allows patch updates only, so 1.2.3 through 1.2.999 are accepted but 1.3.0 is not. Caret is the npm default and the looser range; tilde is what you reach for when a library has a history of breaking minor releases.

Are there size limits for big lockfiles?

Practically yes. The diff runs in your browser, so very large inputs (a 20,000-line lockfile from a monorepo, for example) can slow the page or stall the tab depending on memory. For typical app manifests and lockfiles up to a few thousand lines per side, the diff completes effectively instantly. For larger files, our compare-json page is the better entry point. If you regularly compare huge lockfiles, consider running git diff package-lock.json locally and piping to a pager; that workflow scales further than any browser tool.

How do I diff package.json across an npm workspaces or pnpm monorepo?

Each workspace has its own package.json. Diffing the root manifest only catches dependency hoisting changes and the workspace declarations themselves; per-package changes happen in each packages/*/package.json. For pnpm, pnpm.overrides and pnpm.patchedDependencies at the root affect every workspace, so those merit a separate review. The pragmatic comparison is package-by-package: paste the two versions of one workspace manifest at a time. For lockfile drift, our YAML diff handles pnpm-lock.yaml better since it is huge and YAML-formatted. See the npm workspaces docs for the layout.

Privacy and how this works

Your manifests never leave your browser. The diff, the JSON parsing, the highlighting, and the rendering all run on your machine. We do not upload the text, log it, or pass it to any third-party service. This matters specifically for proprietary code: pasting an unreleased library's package.json or a private repo's lockfile into a cloud service can itself violate your employer's data-handling policy, especially when the manifest names internal scoped packages, private registry hosts, or under-development product names. Verifying the claim is straightforward. Open your browser's DevTools, switch to the Network tab, paste both manifests, and watch. There are no outbound requests when you compare. The same privacy model holds across our other tools, including compare-json, compare-yaml for pnpm-lock.yaml, and git-diff-online for general code review. For the underlying spec, see Yarn's configuration reference and the npm package.json docs.