React components for integrating Microsoft's Monaco Editor into React applications
—
The MonacoDiffEditor component provides side-by-side code comparison functionality with change highlighting, useful for showing differences between code versions, reviewing changes, or implementing merge interfaces.
React component wrapper for Monaco Diff Editor with full TypeScript support and lifecycle management.
/**
* Monaco Diff Editor React component for side-by-side code comparison
* @param props - MonacoDiffEditorProps configuration
* @param ref - Forward ref to access diff editor instance
* @returns React element containing Monaco Diff Editor
*/
declare const MonacoDiffEditor: React.ForwardRefExoticComponent<
MonacoDiffEditorProps & React.RefAttributes<MonacoDiffEditorHandle>
>;Usage Examples:
import React, { useState } from "react";
import { MonacoDiffEditor } from "react-monaco-editor";
// Basic diff editor showing changes
function BasicDiffEditor() {
const originalCode = `function greet(name) {
console.log("Hello " + name);
}`;
const modifiedCode = `function greet(name) {
console.log(\`Hello \${name}!\`);
return \`Hello \${name}!\`;
}`;
return (
<MonacoDiffEditor
width="800"
height="400"
language="javascript"
original={originalCode}
value={modifiedCode}
options={{
renderSideBySide: true,
enableSplitViewResizing: true,
readOnly: false,
}}
/>
);
}
// Interactive diff editor with state management
function InteractiveDiffEditor() {
const [original, setOriginal] = useState('const a = "Hello World";');
const [modified, setModified] = useState('const a = "Hello Monaco";');
return (
<div>
<div>
<button onClick={() => setOriginal('const b = "Updated Original";')}>
Update Original
</button>
<button onClick={() => setModified('const b = "Updated Modified";')}>
Update Modified
</button>
</div>
<MonacoDiffEditor
width="800"
height="300"
language="javascript"
original={original}
value={modified}
onChange={(newValue) => setModified(newValue)}
editorDidMount={(editor, monaco) => {
console.log('Diff editor mounted');
}}
/>
</div>
);
}Full configuration interface for the Monaco Diff Editor component.
interface MonacoDiffEditorProps extends MonacoEditorBaseProps {
/** The original value to compare against. */
original?: string;
/** Value of the auto created model in the editor.
* If you specify value property, the component behaves in controlled mode.
* Otherwise, it behaves in uncontrolled mode. */
value?: string;
/** Refer to Monaco interface {monaco.editor.IDiffEditorConstructionOptions}. */
options?: monaco.editor.IDiffEditorConstructionOptions;
/** Refer to Monaco interface {monaco.editor.IEditorOverrideServices}. */
overrideServices?: monaco.editor.IEditorOverrideServices;
/** An event emitted before the editor mounted (similar to componentWillMount of React). */
editorWillMount?: DiffEditorWillMount;
/** An event emitted when the editor has been mounted (similar to componentDidMount of React). */
editorDidMount?: DiffEditorDidMount;
/** An event emitted before the editor unmount (similar to componentWillUnmount of React). */
editorWillUnmount?: DiffEditorWillUnmount;
/** An event emitted when the content of the current model has changed. */
onChange?: DiffChangeHandler;
/** Let the language be inferred from the uri */
originalUri?: (monaco: typeof monaco) => monaco.Uri;
/** Let the language be inferred from the uri */
modifiedUri?: (monaco: typeof monaco) => monaco.Uri;
}Interface for accessing the Monaco Diff Editor instance via React ref.
interface MonacoDiffEditorHandle {
/** Direct access to Monaco diff editor instance */
editor: monaco.editor.IStandaloneDiffEditor;
}Usage Example:
import React, { useRef } from "react";
import { MonacoDiffEditor } from "react-monaco-editor";
function DiffEditorWithRef() {
const diffEditorRef = useRef<MonacoDiffEditorHandle>(null);
const getChanges = () => {
if (diffEditorRef.current) {
const diffEditor = diffEditorRef.current.editor;
const changes = diffEditor.getLineChanges();
console.log('Line changes:', changes);
// Access individual editors
const originalEditor = diffEditor.getOriginalEditor();
const modifiedEditor = diffEditor.getModifiedEditor();
console.log('Original content:', originalEditor.getValue());
console.log('Modified content:', modifiedEditor.getValue());
}
};
return (
<div>
<button onClick={getChanges}>Get Changes</button>
<MonacoDiffEditor
ref={diffEditorRef}
width="800"
height="400"
language="javascript"
original="const original = true;"
value="const modified = true;"
/>
</div>
);
}Callback functions for handling diff editor lifecycle events.
/**
* Callback invoked before the diff editor is mounted
* @param monaco - Monaco Editor API instance
* @returns Optional editor construction options to merge with props.options
*/
type DiffEditorWillMount = (
monaco: typeof monaco
) => void | monaco.editor.IStandaloneEditorConstructionOptions;
/**
* Callback invoked after the diff editor has been mounted
* @param editor - The mounted Monaco diff editor instance
* @param monaco - Monaco Editor API instance
*/
type DiffEditorDidMount = (
editor: monaco.editor.IStandaloneDiffEditor,
monaco: typeof monaco
) => void;
/**
* Callback invoked before the diff editor is unmounted
* @param editor - The Monaco diff editor instance being unmounted
* @param monaco - Monaco Editor API instance
*/
type DiffEditorWillUnmount = (
editor: monaco.editor.IStandaloneDiffEditor,
monaco: typeof monaco
) => void;
/**
* Callback invoked when the modified editor content changes
* Note: Only changes to the modified (right-side) editor trigger this callback
*/
type DiffChangeHandler = ChangeHandler;Usage Example:
function DiffEditorWithLifecycle() {
const diffEditorWillMount = (monaco) => {
console.log('Diff editor will mount');
// Return options to merge with props.options
return {
renderSideBySide: true,
ignoreTrimWhitespace: false,
};
};
const diffEditorDidMount = (diffEditor, monaco) => {
console.log('Diff editor mounted');
// Access both editors
const originalEditor = diffEditor.getOriginalEditor();
const modifiedEditor = diffEditor.getModifiedEditor();
// Add commands to both editors
const commandId = 'my.custom.command';
originalEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, () => {
console.log('Custom command in original editor');
});
modifiedEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, () => {
console.log('Custom command in modified editor');
});
};
const diffEditorWillUnmount = (diffEditor, monaco) => {
console.log('Diff editor will unmount');
// Cleanup if needed
};
const onChange = (newValue, event) => {
console.log('Modified editor content changed:', newValue);
};
return (
<MonacoDiffEditor
width="800"
height="400"
language="typescript"
original="interface User { name: string; }"
value="interface User { name: string; age: number; }"
editorWillMount={diffEditorWillMount}
editorDidMount={diffEditorDidMount}
editorWillUnmount={diffEditorWillUnmount}
onChange={onChange}
/>
);
}Custom URI creation for both original and modified models, useful for language services and proper file identification.
/**
* Function to create a Monaco URI for the original model
* @param monaco - Monaco Editor API instance
* @returns Monaco URI for the original model
*/
originalUri?: (monaco: typeof monaco) => monaco.Uri;
/**
* Function to create a Monaco URI for the modified model
* @param monaco - Monaco Editor API instance
* @returns Monaco URI for the modified model
*/
modifiedUri?: (monaco: typeof monaco) => monaco.Uri;Usage Example:
function DiffEditorWithURIs() {
return (
<MonacoDiffEditor
width="800"
height="400"
language="typescript"
original="// Original version"
value="// Modified version"
originalUri={(monaco) => monaco.Uri.parse("file:///original.ts")}
modifiedUri={(monaco) => monaco.Uri.parse("file:///modified.ts")}
/>
);
}Frequently used Monaco Diff Editor configuration options:
const commonDiffOptions = {
// Layout
renderSideBySide: true, // Side-by-side view (true) vs inline view (false)
enableSplitViewResizing: true, // Allow resizing the split view
// Whitespace handling
ignoreTrimWhitespace: true, // Ignore leading/trailing whitespace changes
// Line changes
renderIndicators: true, // Show change indicators in overview ruler
// Readonly behavior
readOnly: false, // Allow editing the modified version
originalEditable: false, // Prevent editing the original version
// Diff computation
maxComputationTime: 5000, // Max time in ms for diff computation
// Word-level diffing
diffWordWrap: 'off' as const, // Word wrap for diff algorithm
// Colors and styling
renderOverviewRuler: true, // Show overview ruler with change markers
};The diff editor provides access to both the original and modified editors:
function AccessIndividualEditors() {
const diffEditorRef = useRef<MonacoDiffEditorHandle>(null);
const workWithEditors = () => {
if (diffEditorRef.current) {
const diffEditor = diffEditorRef.current.editor;
// Get individual editors
const originalEditor = diffEditor.getOriginalEditor();
const modifiedEditor = diffEditor.getModifiedEditor();
// Work with original editor
const originalContent = originalEditor.getValue();
originalEditor.setSelection(new monaco.Selection(1, 1, 1, 10));
// Work with modified editor
const modifiedContent = modifiedEditor.getValue();
modifiedEditor.focus();
// Get diff information
const lineChanges = diffEditor.getLineChanges();
console.log('Changes:', lineChanges);
}
};
return (
<div>
<button onClick={workWithEditors}>Work with Editors</button>
<MonacoDiffEditor
ref={diffEditorRef}
width="800"
height="400"
language="javascript"
original="const a = 1;"
value="const b = 2;"
/>
</div>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-monaco-editor