A powerful React component for 3-way JSON merging with semantic comparison, built on Monaco Editor
- π 3-Way Merge - Compare base, theirs, and ours versions side-by-side
- π¨ Monaco Editor Integration - Powered by VS Code's Monaco Editor for a familiar editing experience
- π§ Semantic JSON Comparison - Uses JSON Patch (RFC 6902) for intelligent, structure-aware diffing
- π Schema-Aware - Optional JSON Schema support for enhanced conflict detection and validation
- β Interactive Resolution - Checkboxes for accepting/rejecting changes
- π Smart Merging - Automatically merges compatible conflicts
- β‘ Real-time Validation - JSON validation with error highlighting
- π― 4-Column Mode - Optional result preview column for live merge preview
- π¨ Theme Support - Light and dark themes (VS Code/monaco themes)
- βΏ Accessible - Keyboard navigation and screen reader support
- Zero Line-based Diffs - Uses semantic JSON comparison, ignoring formatting changes
- Array Matching by ID - Schema-aware array item matching (not just by index)
- Conflict Type Detection - Identifies SAME_CHANGE, INPUT1_ONLY, INPUT2_ONLY, and TRUE_CONFLICT
- Deep Merge Support - Automatically merges nested objects when possible
- TypeScript First - Fully typed with comprehensive type definitions
πΊ Try the Live Demo - See the editor in action!
npm install react-monaco-json-mergeimport { JsonDiffMergeEditor } from 'react-monaco-json-merge';
import 'react-monaco-json-merge/dist/style.css';
function App() {
const base = JSON.stringify({ name: "John", age: 30 }, null, 2);
const theirs = JSON.stringify({ name: "John", age: 31, city: "NYC" }, null, 2);
const ours = JSON.stringify({ name: "Jane", age: 30 }, null, 2);
return (
<JsonDiffMergeEditor
base={base}
original={theirs}
modified={ours}
showResultColumn={true}
height="600px"
onMergeResolve={(content, resolution) => {
if (resolution?.isValid) {
console.log('Merged JSON:', JSON.parse(content));
}
}}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
base |
string |
"" |
Common ancestor JSON (stringified) |
original |
string |
"" |
"Theirs" version JSON (stringified) |
modified |
string |
"" |
"Ours" version JSON (stringified) |
showResultColumn |
boolean |
false |
Show merged result preview column |
theme |
string |
"vs" |
Monaco Editor theme ("vs", "vs-dark", "hc-black") |
height |
string | number |
"100%" |
Editor height |
width |
string | number |
"100%" |
Editor width |
schema |
JSONSchema |
undefined |
JSON Schema for validation and array matching |
labels |
object |
undefined |
Custom column labels |
onMergeResolve |
function |
undefined |
Callback when merge resolution changes |
options |
object |
{} |
Monaco Editor options |
comparisonMode |
"split" | "sequential" |
"split" |
How to display the comparison |
baseIndex |
0 | 1 | 2 |
1 |
Position of base column (0=left, 1=middle, 2=right) |
import { JsonDiffMergeEditor } from 'react-monaco-json-merge';
import type { JSONSchema } from 'react-monaco-json-merge';
const schema: JSONSchema = {
type: "object",
properties: {
users: {
type: "array",
items: {
type: "object",
properties: {
id: { type: "string" },
name: { type: "string" }
},
required: ["id"]
}
}
}
};
function AdvancedEditor() {
return (
<JsonDiffMergeEditor
base={baseJSON}
original={theirsJSON}
modified={oursJSON}
schema={schema}
showResultColumn={true}
theme="vs-dark"
height="700px"
labels={{
input1: "Remote Changes",
base: "Common Ancestor",
input2: "Local Changes",
result: "Merged Result"
}}
onMergeResolve={(content, resolution) => {
if (resolution?.isValid) {
// Save merged content
saveToFile(content);
} else {
// Handle validation errors
console.error('Merge has conflicts:', resolution?.conflictIssues);
}
}}
options={{
fontSize: 14,
minimap: { enabled: false },
lineNumbers: 'on'
}}
/>
);
}Unlike traditional diff tools that compare text line-by-line, this editor uses semantic JSON comparison:
- Parses JSON to structured objects
- Generates JSON Patch operations (RFC 6902)
- Groups patches by JSON path
- Maps to line numbers using jsonc-parser
- Applies Monaco decorations based on JSON structure
Benefits:
- Ignores formatting changes (whitespace, key order)
- Schema-aware array matching (by ID, not index)
- Better conflict detection for nested JSON
- Highlights actual value changes, not formatting
The editor identifies four conflict types:
SAME_CHANGE- Both sides made identical changes (auto-merged)INPUT1_ONLY- Only "theirs" changed (can be accepted)INPUT2_ONLY- Only "ours" changed (can be accepted)TRUE_CONFLICT- Both sides changed to different values (requires resolution)
When both checkboxes are selected for a TRUE_CONFLICT:
- If both values are objects: deep merge is performed
- If values are identical: uses either value
- If incompatible types: merge fails, shows warning
- Node.js >= 20.19.0
- npm >= 10.0.0
# Clone the repository
git clone <repository-url>
cd react-monaco-json-merge
# Install dependencies
npm install
# Start development server
npm run dev# Development
npm run dev # Start Vite dev server
npm run build # Build for production
npm run preview # Preview production build
# Code Quality
npm run type-check # TypeScript type checking
npm run lint # Lint code with Biome
npm run lint:fix # Fix linting issues
npm run format # Format code
# Testing
npm run test # Run tests in watch mode
npm run test:run # Run tests once
npm run test:ui # Run tests with UI
npm run test:coverage # Generate coverage report
# Utilities
npm run validate # Run all checks (type-check + lint + test)
npm run clean # Remove build artifactsreact-monaco-json-merge/
βββ src/
β βββ components/
β β βββ editor.tsx # Main JsonDiffMergeEditor component
β βββ data/
β β βββ sampleData.ts # Sample data for demo
β βββ utils/
β β βββ diffMerge.ts # Merge logic
β β βββ jsonPatchDiff.ts # Diff computation
β β βββ editorDecorations.ts # Monaco decorations
β β βββ helpers.ts # Utility functions
β β βββ schema.ts # Schema utilities
β βββ types/
β β βββ index.ts # TypeScript definitions
β βββ styles/
β β βββ editor.css # Editor styles
β βββ Demo.tsx # Demo application
β βββ main.tsx # Entry point
βββ package.json
βββ tsconfig.json
βββ vite.config.ts
βββ README.md
The project includes comprehensive test coverage:
- Unit Tests - Utilities and helpers (94+ tests)
- Integration Tests - Full editor rendering scenarios
- Rendering Tests - Conflict detection and highlighting
- Schema Tests - JSON Schema variant handling
Run tests with:
npm run testView coverage report:
npm run test:coverage- React 19 - UI framework
- Monaco Editor - Code editor
- fast-json-patch - JSON Patch (RFC 6902) implementation
- jsonc-parser - JSON with comments parsing
- fast-deep-equal - Deep equality comparison
- sort-keys - Object key sorting
- TypeScript - Type safety
- Vite - Build tool
- Vitest - Testing framework
- Biome - Linting and formatting
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow TypeScript best practices
- Write tests for new features
- Ensure all tests pass (
npm run validate) - Follow the existing code style (enforced by Biome)
- Update documentation as needed
This project is licensed under the MIT License - see the LICENSE file for details.
- Monaco Editor - The editor that powers this component
- fast-json-patch - JSON Patch implementation
- JSON Schema - Schema validation standard
For issues, questions, or contributions, please open an issue on GitHub.
Made with β€οΈ for better JSON merging experiences