diff --git a/package-lock.json b/package-lock.json
index 890cc30d..1022a75c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,9 +9,13 @@
 			"version": "2.5.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
+				"@codemirror/basic-setup": "^0.20.0",
 				"@codemirror/lang-json": "^6.0.1",
+				"@codemirror/state": "^6.4.1",
+				"@codemirror/view": "^6.33.0",
 				"@uiw/react-codemirror": "^4.23.1",
 				"@wordpress/icons": "^10.7.0",
+				"diff": "^7.0.0",
 				"lib-font": "^2.4.3"
 			},
 			"devDependencies": {
@@ -1981,6 +1985,112 @@
 				"@lezer/common": "^1.0.0"
 			}
 		},
+		"node_modules/@codemirror/basic-setup": {
+			"version": "0.20.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/basic-setup/-/basic-setup-0.20.0.tgz",
+			"integrity": "sha512-W/ERKMLErWkrVLyP5I8Yh8PXl4r+WFNkdYVSzkXYPQv2RMPSkWpr2BgggiSJ8AHF/q3GuApncDD8I4BZz65fyg==",
+			"deprecated": "In version 6.0, this package has been renamed to just 'codemirror'",
+			"dependencies": {
+				"@codemirror/autocomplete": "^0.20.0",
+				"@codemirror/commands": "^0.20.0",
+				"@codemirror/language": "^0.20.0",
+				"@codemirror/lint": "^0.20.0",
+				"@codemirror/search": "^0.20.0",
+				"@codemirror/state": "^0.20.0",
+				"@codemirror/view": "^0.20.0"
+			}
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@codemirror/autocomplete": {
+			"version": "0.20.3",
+			"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-0.20.3.tgz",
+			"integrity": "sha512-lYB+NPGP+LEzAudkWhLfMxhTrxtLILGl938w+RcFrGdrIc54A+UgmCoz+McE3IYRFp4xyQcL4uFJwo+93YdgHw==",
+			"dependencies": {
+				"@codemirror/language": "^0.20.0",
+				"@codemirror/state": "^0.20.0",
+				"@codemirror/view": "^0.20.0",
+				"@lezer/common": "^0.16.0"
+			}
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@codemirror/commands": {
+			"version": "0.20.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-0.20.0.tgz",
+			"integrity": "sha512-v9L5NNVA+A9R6zaFvaTbxs30kc69F6BkOoiEbeFw4m4I0exmDEKBILN6mK+GksJtvTzGBxvhAPlVFTdQW8GB7Q==",
+			"dependencies": {
+				"@codemirror/language": "^0.20.0",
+				"@codemirror/state": "^0.20.0",
+				"@codemirror/view": "^0.20.0",
+				"@lezer/common": "^0.16.0"
+			}
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@codemirror/language": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-0.20.2.tgz",
+			"integrity": "sha512-WB3Bnuusw0xhVvhBocieYKwJm04SOk5bPoOEYksVHKHcGHFOaYaw+eZVxR4gIqMMcGzOIUil0FsCmFk8yrhHpw==",
+			"dependencies": {
+				"@codemirror/state": "^0.20.0",
+				"@codemirror/view": "^0.20.0",
+				"@lezer/common": "^0.16.0",
+				"@lezer/highlight": "^0.16.0",
+				"@lezer/lr": "^0.16.0",
+				"style-mod": "^4.0.0"
+			}
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@codemirror/lint": {
+			"version": "0.20.3",
+			"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-0.20.3.tgz",
+			"integrity": "sha512-06xUScbbspZ8mKoODQCEx6hz1bjaq9m8W8DxdycWARMiiX1wMtfCh/MoHpaL7ws/KUMwlsFFfp2qhm32oaCvVA==",
+			"dependencies": {
+				"@codemirror/state": "^0.20.0",
+				"@codemirror/view": "^0.20.2",
+				"crelt": "^1.0.5"
+			}
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@codemirror/search": {
+			"version": "0.20.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-0.20.1.tgz",
+			"integrity": "sha512-ROe6gRboQU5E4z6GAkNa2kxhXqsGNbeLEisbvzbOeB7nuDYXUZ70vGIgmqPu0tB+1M3F9yWk6W8k2vrFpJaD4Q==",
+			"dependencies": {
+				"@codemirror/state": "^0.20.0",
+				"@codemirror/view": "^0.20.0",
+				"crelt": "^1.0.5"
+			}
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@codemirror/state": {
+			"version": "0.20.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz",
+			"integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ=="
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@codemirror/view": {
+			"version": "0.20.7",
+			"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz",
+			"integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==",
+			"dependencies": {
+				"@codemirror/state": "^0.20.0",
+				"style-mod": "^4.0.0",
+				"w3c-keyname": "^2.2.4"
+			}
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@lezer/common": {
+			"version": "0.16.1",
+			"resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.16.1.tgz",
+			"integrity": "sha512-qPmG7YTZ6lATyTOAWf8vXE+iRrt1NJd4cm2nJHK+v7X9TsOF6+HtuU/ctaZy2RCrluxDb89hI6KWQ5LfQGQWuA=="
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@lezer/highlight": {
+			"version": "0.16.0",
+			"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-0.16.0.tgz",
+			"integrity": "sha512-iE5f4flHlJ1g1clOStvXNLbORJoiW4Kytso6ubfYzHnaNo/eo5SKhxs4wv/rtvwZQeZrK3we8S9SyA7OGOoRKQ==",
+			"dependencies": {
+				"@lezer/common": "^0.16.0"
+			}
+		},
+		"node_modules/@codemirror/basic-setup/node_modules/@lezer/lr": {
+			"version": "0.16.3",
+			"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.16.3.tgz",
+			"integrity": "sha512-pau7um4eAw94BEuuShUIeQDTf3k4Wt6oIUOYxMmkZgDHdqtIcxWND4LRxi8nI9KuT4I1bXQv67BCapkxt7Ywqw==",
+			"dependencies": {
+				"@lezer/common": "^0.16.0"
+			}
+		},
 		"node_modules/@codemirror/commands": {
 			"version": "6.5.0",
 			"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz",
@@ -2051,9 +2161,9 @@
 			}
 		},
 		"node_modules/@codemirror/view": {
-			"version": "6.26.3",
-			"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz",
-			"integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==",
+			"version": "6.33.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.33.0.tgz",
+			"integrity": "sha512-AroaR3BvnjRW8fiZBalAaK+ZzB5usGgI014YKElYZvQdNH5ZIidHlO+cyf/2rWzyBFRkvG6VhiXeAEbC53P2YQ==",
 			"dependencies": {
 				"@codemirror/state": "^6.4.0",
 				"style-mod": "^4.1.0",
@@ -8683,6 +8793,14 @@
 			"integrity": "sha512-oD9vGBV2wTc7fAzAM6KC0chSgs234V8+qDEeK+mcbRj2UvcuA7lgBztGi/opj/iahcXD3BSj8Ymvib628yy9FA==",
 			"dev": true
 		},
+		"node_modules/diff": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
+			"integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
+			"engines": {
+				"node": ">=0.3.1"
+			}
+		},
 		"node_modules/diff-sequences": {
 			"version": "29.6.3",
 			"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
diff --git a/package.json b/package.json
index f016b13e..7422df34 100644
--- a/package.json
+++ b/package.json
@@ -19,9 +19,13 @@
 		"npm": ">=10.2.3"
 	},
 	"dependencies": {
+		"@codemirror/basic-setup": "^0.20.0",
 		"@codemirror/lang-json": "^6.0.1",
+		"@codemirror/state": "^6.4.1",
+		"@codemirror/view": "^6.33.0",
 		"@uiw/react-codemirror": "^4.23.1",
 		"@wordpress/icons": "^10.7.0",
+		"diff": "^7.0.0",
 		"lib-font": "^2.4.3"
 	},
 	"devDependencies": {
diff --git a/src/editor-sidebar/code-mirror-diff-viewer.js b/src/editor-sidebar/code-mirror-diff-viewer.js
new file mode 100644
index 00000000..9d765b9a
--- /dev/null
+++ b/src/editor-sidebar/code-mirror-diff-viewer.js
@@ -0,0 +1,133 @@
+/**
+ * External dependencies
+ */
+import CodeMirror from '@uiw/react-codemirror';
+import { EditorView, ViewPlugin, Decoration } from '@codemirror/view';
+import { EditorState, RangeSetBuilder } from '@codemirror/state';
+import { basicSetup } from '@codemirror/basic-setup';
+import { json } from '@codemirror/lang-json';
+import { diffLines } from 'diff';
+
+/**
+ * WordPress dependencies
+ */
+import { __, sprintf } from '@wordpress/i18n';
+import { useState, useEffect } from '@wordpress/element';
+
+export function CodeMirrorDiffViewer({ oldCode, newCode }) {
+    const [diff, setDiff] = useState([]);
+
+    useEffect(() => {
+        // override for testing
+        const oldCode = {
+            "hello": "123",
+            "world": "456",
+            "foo": "bar"
+        };
+        const newCode = {
+            "hello": "123",
+            "world": "456",
+            "foo": "baz"
+        };
+
+        setDiff(diffLines(JSON.stringify(oldCode, null, 4), JSON.stringify(newCode, null, 4)));
+    }, [oldCode, newCode]);
+
+    const diffDecorations = EditorView.decorations.compute([], (state) => getDiffDecorations(diff));
+    // const diffDecorations = (diff) => {
+    //     const builder = new RangeSetBuilder();
+    //     diff.forEach((part, index) => {
+    //         if (part.added || part.removed) {
+    //             const className = part.added ? 'added' : 'removed';
+    //             const decoration = Decoration.mark({
+    //                 class: className,
+    //             });
+    //             builder.add(index, index + part.value.length, decoration);
+    //         }
+    //     });
+    //     return builder.finish();
+    // };
+
+    // const diffPlugin = ViewPlugin.fromClass(class {
+    //     constructor(view) {
+    //         this.decorations = diffDecorations(diff);
+    //     }
+    //     update(update) {
+    //         if (update.docChanged || update.viewportChanged) {
+    //             this.decorations = diffDecorations(diff);
+    //         }
+    //     }
+    // }, {
+    //     decorations: v => v.decorations
+    // });
+
+    // const state = EditorState.create({
+    //     doc: newCode,
+    //     extensions: [basicSetup, json(), diffPlugin]
+    // });
+
+    // return <CodeMirror state={state} />;
+
+    return (
+        <div>
+            <CodeMirror
+                value={ diff.map(part => part.value).join('') }
+                extensions={ [ json(), diffDecorations ] }
+				readOnly
+			/>
+            <style>
+                {`
+                .cbt-code-mirror-line-added { background-color: #D1F8D9; }
+                .cbt-code-mirror-line-removed { background-color: #FFCECB; }
+                `}
+            </style>
+        </div>
+    );
+}
+
+export function getDiffDecorations(diff) {
+    let decorations = [];
+    let lineNumber = 0;
+
+    diff.forEach(part => {
+        const lines = part.value.split('\n');
+        lines.forEach((line, index) => {
+            if (line === '') return; // Skip empty lines
+
+            const from = lineNumber + index;
+            const to = from; // Single line
+
+            if (part.added) {
+                decorations.push(Decoration.line({ class: "cbt-code-mirror-line-added" }).range(from, to));
+            } else if (part.removed) {
+                decorations.push(Decoration.line({ class: "cbt-code-mirror-line-removed" }).range(from, to));
+            }
+        });
+
+        lineNumber += lines.length - 1;
+    });
+
+    return Decoration.set(decorations);
+}
+
+export function mergeDeep(target, ...sources) {
+	if (!sources.length) return target;
+	const source = sources.shift();
+
+	if (isObject(target) && isObject(source)) {
+		for (const key in source) {
+            if (isObject(source[key])) {
+                if (!target[key]) Object.assign(target, { [key]: {} });
+                mergeDeep(target[key], source[key]);
+            } else {
+                Object.assign(target, { [key]: source[key] });
+            }
+		}
+	}
+
+	return mergeDeep(target, ...sources);
+}
+
+function isObject(item) {
+	return (item && typeof item === 'object' && !Array.isArray(item));
+}
diff --git a/src/editor-sidebar/json-editor-modal.js b/src/editor-sidebar/json-editor-modal.js
index 11d367ca..f6c4d1b0 100644
--- a/src/editor-sidebar/json-editor-modal.js
+++ b/src/editor-sidebar/json-editor-modal.js
@@ -1,9 +1,3 @@
-/**
- * External dependencies
- */
-import CodeMirror from '@uiw/react-codemirror';
-import { json } from '@codemirror/lang-json';
-
 /**
  * WordPress dependencies
  */
@@ -16,6 +10,7 @@ import { useSelect } from '@wordpress/data';
  * Internal dependencies
  */
 import { fetchThemeJson } from '../resolvers';
+import { CodeMirrorDiffViewer } from './code-mirror-diff-viewer';
 
 const ThemeJsonEditorModal = ( { onRequestClose } ) => {
 	const [ themeData, setThemeData ] = useState( '' );
@@ -42,12 +37,7 @@ const ThemeJsonEditorModal = ( { onRequestClose } ) => {
 			onRequestClose={ onRequestClose }
 			className="create-block-theme__theme-json-modal"
 		>
-			<CodeMirror
-				extensions={ [ json() ] }
-				value={ themeData }
-				onChange={ handleSave }
-				readOnly
-			/>
+			<CodeMirrorDiffViewer oldCode={JSON.stringify(themeData, null, 2)} newCode={JSON.stringify(themeData, null, 2)} />
 		</Modal>
 	);
 };