jsondiffpatch: the delta format, array matching, and a package stuck on 0.6.0
Diff & patch JavaScript objects
At a glance
- What is it?
- A 16KB TypeScript library that turns two JavaScript values into a delta you can apply, reverse and reformat into HTML, annotated JSON or RFC 6902. Its array diffing depends entirely on one function you have to remember to supply.
- Who is it for?
- The design is sound and the footprint claim holds, but the packaging has drifted from the code. The repository was last pushed on 2026-05-14, while the newest published release is v0.6.0 from 2023-12-15, so anyone reading the README for current API shape is reading three years of unreleased drift.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 145 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One idea, kept small on purpose
jsondiffpatch answers a narrow question and refuses to widen it. Given two JavaScript values, what is the smallest record of how to turn the first into the second? That record is a delta, a plain JSON object holding only what changed, and the library gives you the tools to produce it, apply it, reverse it, and inspect it. The README states the cost up front as min+gzipped ~ 16KB with browser and server (ESM-only) support, which is a fair baseline in a category where diffing libraries usually arrive with a dependency tree attached.
Installing it is a single package, and the CLI is reachable without installing anything at all:
npm install jsondiffpatchnpx jsondiffpatch --helpThe repository is TypeScript, MIT licensed, 5343 stars and 496 forks, on default branch master, not archived, last pushed 2026-05-14, with 54 open issues. The topics list is delta, diff, diffing, json, jsondiffpatch, patch and text-diff, which is an accurate summary of the surface area. The API is correspondingly narrow: diff, patch, unpatch, reverse, clone, create and dateReviver. There is no plugin marketplace, no built-in transport, no opinion about how you persist a delta. The format is the product.
Configuration happens through create, which takes an options object and returns an instance. That indirection is what lets several parts of the same application attach different objectHash functions, array policies or text diff settings without fighting over module state, and it is the difference between a library that embeds in a React app and one that only works in a script. The demo at jsondiffpatch.com is built from demos/html-demo and is the fastest way to see what a real delta looks like against real documents.
Array diffing is the part that decides whether you trust the output
The README puts this warning in bold italics for good reason: smart array diffing uses LCS, and to match objects inside an array you must provide an objectHash function. Without one, matching falls back to position, which is not a degraded feature so much as a different and mostly wrong answer.
Consider the array example in the README, five Argentine cities that get one deleted, one inserted, one modified and one moved. With objectHash configured to return obj.name, the delta correctly records an insertion at index 1, a population change at index 2, a removal that keeps the original index with the underscore prefix, and a separate move entry carrying an empty string plus the two indices. That last entry is the move, detected because arrays.detectMove defaults to true. If you had relied on positional matching, the modified city and the moved city would each look like a deletion paired with an insertion, the delta would be larger, and applying it would still land on the correct final state while carrying none of the history you thought you had. Silent and plausible is the dangerous failure mode here.
Two array options shape the output. detectMove, default true, controls whether moved items are recognised at all. includeValueOnMove, default false, controls whether the moved item itself is included in the delta, which is a bandwidth decision rather than a correctness one. Nested arrays carry an _t marker of a so that the patcher knows a node is an array and not an object, since both are JSON objects once serialised. The tests that pin all of this behaviour are not at the repository root; the README sends you to packages/jsondiffpatch/test/examples for nested objects, arrays and long text diffs, which is the right place to look before trusting an edge case.
Five output formats over a single engine
The delta is computed once and then rendered, which is why the formatter list is so much longer than the algorithm list. The default output is pure JSON in the delta format, described as a low footprint option and documented in docs/deltas.md. From there the same delta can be rendered as an HTML visual diff, as annotated JSON that explains each part of the delta format inline, as RFC 6902 JSON Patch that can be generated and also applied, or as coloured console output that the README shows running as ./node_modules/.bin/jsondiffpatch left.json right.json. The docs at docs/formatters.md cover writing your own, and the subpath layout in 0.6.0 gives each of them a separate entry point.
Long strings get an optional second layer. Text diffing runs at character level through google diff match patch, which is no longer bundled: you either pass diff_match_patch in through textDiff.diffMatchPatch, taking it from @dmsnell/diff-match-patch, or import from the jsondiffpatch/with-text-diffs subpath so the dependency comes along. A minimum string length option, default 60 for both sides, keeps the character level pass from firing on values where it would cost more than it explains.
Dates are handled by dateReviver, which you pass as the reviver to JSON.parse when cloning an object that contains Date instances. The README example makes the reason visible: it clones country with JSON.parse(JSON.stringify(country), jsondiffpatch.dateReviver), and the resulting delta shows the new population value as a quoted string. What has actually happened is that the round trip has changed a number into a string, so the delta is faithfully reporting a change in type as well as in value. That is a fair illustration of how much representation detail the delta format captures, and also of why cloning through JSON is a decision rather than a shortcut.
Version 0.6.0 broke six things on purpose
The newest release, v0.6.0 published 2023-12-15, is a single breaking-changes pull request, number 350, and it reshapes the whole import surface. The package becomes pure ESM, with a link to Sindre Sorhus's FAQ on why. Supported Node versions narrow to ^18.0.0 || >=20.0.0. ES6 support becomes a requirement. There is no longer a default export, so the documented form becomes a namespace import of jsondiffpatch, or importing individual methods.
The most disruptive change is that formatters leave the main entry point entirely and move to subpaths, each with its own explicit import: jsondiffpatch/formatters/annotated, jsondiffpatch/formatters/base, jsondiffpatch/formatters/console, jsondiffpatch/formatters/html and jsondiffpatch/formatters/jsonpatch. CSS follows the same pattern, with jsondiffpatch/formatters/styles/html.css and jsondiffpatch/formatters/styles/annotated.css as separate imports. Text diffing is also removed from the main entry point, which is the change that produces the subpath alternative described above.
Two things are worth noticing about the state of the project. The published release body for v0.6.0 is cut off mid sentence at the textDiff option, so the migration notes are incomplete and the second half of that option list exists only in the README. And the repository has been pushed to as recently as 2026-05-14, more than two and a half years after the tag. That gap between active commits and an unchanged npm version is the single most useful thing an adopter can know: the code you get from the registry is not necessarily the code on master, and the README documents the current state rather than the shipped one.
The monorepo shows where the work moved
The root package.json is short enough to read in full, and it explains the repository layout. It declares npm workspaces covering demos/console-demo, demos/html-demo, demos/numeric-plugin-demo, packages/jsondiffpatch and packages/diff-mcp. Three of those five are demos, which says something about how much of this project is about showing the output rather than producing it. The fifth, packages/diff-mcp, is a separate package for Model Context Protocol exposure, a direction that has nothing to do with the delta format and a great deal to do with where attention in the ecosystem is going.
Only two scripts are declared at the root, and neither runs the tests. lint maps to biome check --error-on-warnings ., so formatting and lint failures are hard errors rather than warnings, and type-check maps to tsc --noEmit. The devDependencies are correspondingly lean, @biomejs/biome at ^1.9.4 and typescript at ^5.8.2. The repository also carries biome.jsonc, tsconfig.json, .editorconfig, .npmrc and .gitignore, which is a modern and tidy setup. That there is no test script at the root is not an oversight so much as a consequence of testing inside packages/jsondiffpatch, but it does mean a contributor has to know where to look.
The two demos beyond console and html earn their place. numeric-plugin-demo exists because numbers were historically a weak point in diffing, and showing that gap can be closed through a plugin is a better argument than a changelog entry. console-demo backs the CLI example in the README. The structure is coherent, and the one surprise is diff-mcp, which suggests the maintainers see the future of this library as something an agent can call rather than something an application imports.
What the 54 open issues are probably about
With 54 open issues against 5343 stars, this is a mature library with a normal amount of accumulated friction rather than a neglected one. The evidence points to three recurring themes. The first is the ESM boundary. A pure ESM package with no default export and five subpath imports is a common source of reports from CommonJS consumers and from bundlers that resolve exports maps differently, and the 0.6.0 notes show the maintainers had to enumerate the migration carefully.
The second is array matching, and it is the theme this repository is most exposed on. objectHash is optional and its absence produces a result that looks plausible, so a surprising share of issues will be about a delta that is technically correct and semantically wrong. Move detection with includeValueOnMove set to false is a related source of confusion, since the moved item is absent from the delta by default and callers expecting to recover it from the delta alone will not.
The third is dates and other non-JSON types. The library can round trip a Date through dateReviver, but the delta format itself is JSON, so anything not representable in JSON has to be handled either by custom formatters or by accepting that the diff describes what the serialised value looks like rather than what the in-memory value is. TypeScript 5.8 and Biome 1.9 in the root devDependencies also suggest the toolchain is not current, so typing edge cases with newer syntax are a plausible source of reports as well.
None of this makes the library a poor choice. It is small, focused, MIT licensed, well documented through docs/deltas.md, docs/arrays.md and docs/formatters.md, and tested against example cases that live in a path the README actually points you to. The sharp edges are documented edges, and the two that will cost you the most time are supplying objectHash and pinning the version you actually tested against.
Editorial conclusion
The design is sound and the footprint claim holds, but the packaging has drifted from the code. The repository was last pushed on 2026-05-14, while the newest published release is v0.6.0 from 2023-12-15, so anyone reading the README for current API shape is reading three years of unreleased drift. Two concrete details should decide your approach. First, supply an objectHash function or your array diffs will silently degrade to positional matching and record every move as a delete plus an insert. Second, the root package.json exposes only lint and type-check, so the test suite you would want to lean on lives under packages/jsondiffpatch rather than at the top of the monorepo. Install with npm install jsondiffpatch, pin the version deliberately, and read docs/deltas.md before you rely on the wire format in production.
Frequently asked questions
What is a JSON Patch?
JSON Patch is the format defined in RFC 6902, a list of operations such as add, remove and replace, each pointing at a path inside a document. It is a standard that other systems can apply without knowing anything about jsondiffpatch. This library generates and applies it through the jsonpatch formatter, but that is only one of its output formats.
What's the difference between diff and patch?
Diff takes two values and returns a delta describing the difference between them. Patch takes a delta and applies it to a value, mutating it in place, so the country object in the README example becomes equal to the clone. Reverse turns a delta upside down, and unpatch uses a delta to roll a value back to where it started.
What are the key differences between the JSON Patch and JSON merge patch methods?
JSON Patch in RFC 6902 is an operation list with explicit paths, so it can insert, move and reorder without ambiguity. A JSON merge patch is simpler: it is a partial document where a null value means delete and anything else means replace. jsondiffpatch's own delta format is neither, it is a compact internal encoding, and RFC 6902 output is available as one formatter among five.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/benjamine-jsondiffpatch)