
Security News
OWASP 2025 Top 10 Adds Software Supply Chain Failures, Ranked Top Community Concern
OWASP’s 2025 Top 10 introduces Software Supply Chain Failures as a new category, reflecting rising concern over dependency and build system risks.
json-diff-ts
Advanced tools
Modern TypeScript JSON diff library - Zero dependencies, high performance, ESM + CommonJS support. Calculate and apply differences between JSON objects with advanced features like key-based array diffing, JSONPath support, and atomic changesets.
Modern TypeScript JSON diff library - json-diff-ts is a lightweight, high-performance TypeScript library for calculating and applying differences between JSON objects. Perfect for modern web applications, state management, data synchronization, and real-time collaborative editing.
This library is particularly valuable for applications where tracking changes in JSON data is crucial, such as state management systems, form handling, or data synchronization.
npm install json-diff-ts
import { diff, applyChangeset } from 'json-diff-ts';
// Two versions of data
const oldData = { name: 'Luke', level: 1, skills: ['piloting'] };
const newData = { name: 'Luke Skywalker', level: 5, skills: ['piloting', 'force'] };
// Calculate differences
const changes = diff(oldData, newData);
console.log(changes);
// Output: [
// { type: 'UPDATE', key: 'name', value: 'Luke Skywalker', oldValue: 'Luke' },
// { type: 'UPDATE', key: 'level', value: 5, oldValue: 1 },
// { type: 'ADD', key: 'skills', value: 'force', embeddedKey: '1' }
// ]
// Apply changes to get the new object
const result = applyChangeset(oldData, changes);
console.log(result); // { name: 'Luke Skywalker', level: 5, skills: ['piloting', 'force'] }
TypeScript / ES Modules:
import { diff } from 'json-diff-ts';
CommonJS:
const { diff } = require('json-diff-ts');
diffGenerates a difference set for JSON objects. When comparing arrays, if a specific key is provided, differences are determined by matching elements via this key rather than array indices.
import { diff } from 'json-diff-ts';
// State during A New Hope - Desert planet, small rebel cell
const oldData = {
location: 'Tatooine',
mission: 'Rescue Princess',
status: 'In Progress',
characters: [
{ id: 'LUKE_SKYWALKER', name: 'Luke Skywalker', role: 'Farm Boy', forceTraining: false },
{ id: 'LEIA_ORGANA', name: 'Princess Leia', role: 'Prisoner', forceTraining: false }
],
equipment: ['Lightsaber', 'Blaster']
};
// State after successful rescue - Base established, characters evolved
const newData = {
location: 'Yavin Base',
mission: 'Destroy Death Star',
status: 'Complete',
characters: [
{ id: 'LUKE_SKYWALKER', name: 'Luke Skywalker', role: 'Pilot', forceTraining: true, rank: 'Commander' },
{ id: 'HAN_SOLO', name: 'Han Solo', role: 'Smuggler', forceTraining: false, ship: 'Millennium Falcon' }
],
equipment: ['Lightsaber', 'Blaster', 'Bowcaster', 'X-wing Fighter']
};
const diffs = diff(oldData, newData, { embeddedObjKeys: { characters: 'id' } });
console.log(diffs);
// First operations:
// [
// { type: 'UPDATE', key: 'location', value: 'Yavin Base', oldValue: 'Tatooine' },
// { type: 'UPDATE', key: 'mission', value: 'Destroy Death Star', oldValue: 'Rescue Princess' },
// { type: 'UPDATE', key: 'status', value: 'Complete', oldValue: 'In Progress' },
// ...
// ]
import { diff } from 'json-diff-ts';
// Using nested paths for sub-arrays
const diffs = diff(oldData, newData, { embeddedObjKeys: { 'characters.equipment': 'id' } });
// Designating root with '.' - useful for complex nested structures
const diffs = diff(oldData, newData, { embeddedObjKeys: { '.characters.allies': 'id' } });
import { diff } from 'json-diff-ts';
// Control how type changes are treated
const diffs = diff(oldData, newData, { treatTypeChangeAsReplace: false });
Date objects can now be updated to primitive values without errors when treatTypeChangeAsReplace is set to false.
import { diff } from 'json-diff-ts';
// Skip specific nested paths from comparison - useful for ignoring metadata
const diffs = diff(oldData, newData, { keysToSkip: ['characters.metadata'] });
import { diff } from 'json-diff-ts';
// Use function to resolve object keys dynamically
const diffs = diff(oldData, newData, {
embeddedObjKeys: {
characters: (obj, shouldReturnKeyName) => (shouldReturnKeyName ? 'id' : obj.id)
}
});
import { diff } from 'json-diff-ts';
// Use regex for path matching - powerful for dynamic property names
const embeddedObjKeys = new Map();
embeddedObjKeys.set(/^characters/, 'id'); // Match any property starting with 'characters'
const diffs = diff(oldData, newData, { embeddedObjKeys });
import { diff } from 'json-diff-ts';
// Compare string arrays by value instead of index - useful for tags, categories
const diffs = diff(oldData, newData, { embeddedObjKeys: { equipment: '$value' } });
atomizeChangeset and unatomizeChangesetTransform complex changesets into a list of atomic changes (and back), each describable by a JSONPath.
import { atomizeChangeset, unatomizeChangeset } from 'json-diff-ts';
// Create atomic changes
const atomicChanges = atomizeChangeset(diffs);
// Restore the changeset from a selection of atomic changes
const changeset = unatomizeChangeset(atomicChanges.slice(0, 3));
Atomic Changes Structure:
[
{
type: 'UPDATE',
key: 'location',
value: 'Yavin Base',
oldValue: 'Tatooine',
path: '$.location',
valueType: 'String'
},
{
type: 'UPDATE',
key: 'mission',
value: 'Destroy Death Star',
oldValue: 'Rescue Princess',
path: '$.mission',
valueType: 'String'
},
{
type: 'ADD',
key: 'rank',
value: 'Commander',
path: "$.characters[?(@.id=='LUKE_SKYWALKER')].rank",
valueType: 'String'
},
{
type: 'ADD',
key: 'HAN_SOLO',
value: { id: 'HAN_SOLO', name: 'Han Solo', role: 'Smuggler', forceTraining: false, ship: 'Millennium Falcon' },
path: "$.characters[?(@.id=='HAN_SOLO')]",
valueType: 'Object'
}
]
applyChangeset and revertChangesetApply or revert changes to JSON objects.
import { applyChangeset, revertChangeset } from 'json-diff-ts';
// Apply changes
const updated = applyChangeset(oldData, diffs);
console.log(updated);
// { location: 'Yavin Base', mission: 'Destroy Death Star', status: 'Complete', ... }
// Revert changes
const reverted = revertChangeset(newData, diffs);
console.log(reverted);
// { location: 'Tatooine', mission: 'Rescue Princess', status: 'In Progress', ... }
| Function | Description | Parameters |
|---|---|---|
diff(oldObj, newObj, options?) | Generate differences between two objects | oldObj: Original objectnewObj: Updated objectoptions: Optional configuration |
applyChangeset(obj, changeset) | Apply changes to an object | obj: Object to modifychangeset: Changes to apply |
revertChangeset(obj, changeset) | Revert changes from an object | obj: Object to modifychangeset: Changes to revert |
atomizeChangeset(changeset) | Convert changeset to atomic changes | changeset: Nested changeset |
unatomizeChangeset(atomicChanges) | Convert atomic changes back to nested changeset | atomicChanges: Array of atomic changes |
| Function | Description | Parameters |
|---|---|---|
compare(oldObj, newObj) | Create enriched comparison object | oldObj: Original objectnewObj: Updated object |
enrich(obj) | Create enriched representation of object | obj: Object to enrich |
createValue(value) | Create value node for comparison | value: Any value |
createContainer(value) | Create container node for comparison | value: Object or Array |
interface Options {
embeddedObjKeys?: Record<string, string | Function> | Map<string | RegExp, string | Function>;
keysToSkip?: string[];
treatTypeChangeAsReplace?: boolean;
}
| Option | Type | Description |
|---|---|---|
embeddedObjKeys | `Record<string, string | Function>orMap<string |
keysToSkip | string[] | Dotted paths to exclude from comparison, e.g. "meta.info". |
treatTypeChangeAsReplace | boolean | When true (default), a type change results in a REMOVE/ADD pair. Set to false to treat it as an UPDATE. |
enum Operation {
REMOVE = 'REMOVE',
ADD = 'ADD',
UPDATE = 'UPDATE'
}
v4.8.2: Fixed array handling in applyChangeset for null, undefined, and deleted elements (fixes issue #316)
v4.8.1: Improved documentation with working examples and detailed options.
v4.8.0: Significantly reduced bundle size by completely removing es-toolkit dependency and implementing custom utility functions. This change eliminates external dependencies while maintaining identical functionality and improving performance.
v4.7.0: Optimized bundle size and performance by replacing es-toolkit/compat with es-toolkit for difference, intersection, and keyBy functions
v4.6.3: Fixed null comparison returning update when values are both null (fixes issue #284)
v4.6.2: Fixed updating to null when treatTypeChangeAsReplace is false and bumped Jest dev dependencies
v4.6.1: Consistent JSONPath format for array items (fixes issue #269)
v4.6.0: Fixed filter path regex to avoid polynomial complexity
v4.5.1: Updated package dependencies
v4.5.0: Switched internal utilities from lodash to es-toolkit/compat for a smaller bundle size
v4.4.0: Fixed Date-to-string diff when treatTypeChangeAsReplace is false
v4.3.0: Enhanced functionality:
v4.2.0: Improved stability with multiple fixes:
v4.1.0: Full support for ES modules while maintaining CommonJS compatibility
v4.0.0: Changed naming of flattenChangeset and unflattenChanges to atomizeChangeset and unatomizeChangeset; added option to set treatTypeChangeAsReplace
v3.0.1: Fixed issue with unflattenChanges when a key has periods
v3.0.0: Added support for both CommonJS and ECMAScript Modules. Replaced lodash-es with lodash to support both module formats
v2.2.0: Fixed lodash-es dependency, added exclude keys option, added string array comparison by value
v2.1.0: Fixed JSON Path filters by replacing single equal sign (=) with double equal sign (==). Added support for using '.' as root in paths
v2.0.0: Upgraded to ECMAScript module format with optimizations and improved documentation. Fixed regex path handling (breaking change: now requires Map instead of Record for regex paths)
v1.2.6: Enhanced JSON Path handling for period-inclusive segments
v1.2.5: Added key name resolution support for key functions
v1.2.4: Documentation updates and dependency upgrades
v1.2.3: Updated dependencies and TypeScript
Contributions are welcome! Please follow the provided issue templates and code of conduct.
| Feature | json-diff-ts | deep-diff | jsondiffpatch |
|---|---|---|---|
| TypeScript | ✅ Native | ❌ Partial | ❌ Definitions only |
| Bundle Size | 🟢 21KB | 🟡 45KB | 🔴 120KB+ |
| Dependencies | 🟢 Zero | 🟡 Few | 🔴 Many |
| ESM Support | ✅ Native | ❌ CJS only | ❌ CJS only |
| Array Key Matching | ✅ Advanced | ❌ Basic | ✅ Advanced |
| JSONPath Support | ✅ Full | ❌ None | ❌ Limited |
Q: Can I use this with React/Vue/Angular?
A: Yes! json-diff-ts works with any JavaScript framework or vanilla JS.
Q: Does it work with Node.js?
A: Absolutely! Supports Node.js 18+ with both CommonJS and ES modules.
Q: How does it compare to JSON Patch (RFC 6902)?
A: json-diff-ts provides a more flexible format with advanced array handling, while JSON Patch is a standardized format.
Q: Is it suitable for large objects?
A: Yes, the library is optimized for performance and can handle large, complex JSON structures efficiently.
Reach out to the maintainer:
Discover more about the company behind this project: hololux
This project takes inspiration and code from diff-json by viruschidai@gmail.com.
json-diff-ts is open-sourced software licensed under the MIT license.
The original diff-json project is also under the MIT License. For more information, refer to its license details.
FAQs
Modern TypeScript JSON diff library - Zero dependencies, high performance, ESM + CommonJS support. Calculate and apply differences between JSON objects with advanced features like key-based array diffing, JSONPath support, and atomic changesets.
The npm package json-diff-ts receives a total of 257,883 weekly downloads. As such, json-diff-ts popularity was classified as popular.
We found that json-diff-ts demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OWASP’s 2025 Top 10 introduces Software Supply Chain Failures as a new category, reflecting rising concern over dependency and build system risks.

Research
/Security News
Socket researchers discovered nine malicious NuGet packages that use time-delayed payloads to crash applications and corrupt industrial control systems.

Security News
Socket CTO Ahmad Nassri discusses why supply chain attacks now target developer machines and what AI means for the future of enterprise security.