Monaco Editor for React - use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Side-by-side code comparison editor for visualizing differences between two versions of code. Perfect for code reviews, version comparisons, and merge conflict resolution.
Monaco Diff Editor component that displays two code versions side-by-side with highlighted differences.
/**
* Monaco Diff Editor React component for comparing two code versions
* @param props - Diff editor configuration and content
* @returns React component rendering Monaco Diff Editor
*/
declare const DiffEditor: React.ComponentType<DiffEditorProps>;
interface DiffEditorProps {
/** Original source (left side) value */
original?: string;
/** Modified source (right side) value */
modified?: string;
/** Language for both models */
language?: string;
/** Language for original model only */
originalLanguage?: string;
/** Language for modified model only */
modifiedLanguage?: string;
/** Path for the original model */
originalModelPath?: string;
/** Path for the modified model */
modifiedModelPath?: string;
/** Whether to keep original model when unmounting */
keepCurrentOriginalModel?: boolean;
/** Whether to keep modified model when unmounting */
keepCurrentModifiedModel?: boolean;
/** Theme for the monaco editor */
theme?: Theme | string;
/** Loading component shown before editor is mounted */
loading?: ReactNode;
/** Diff editor construction options */
options?: editor.IDiffEditorConstructionOptions;
/** Width of editor wrapper */
width?: number | string;
/** Height of editor wrapper */
height?: number | string;
/** CSS class name for editor container */
className?: string;
/** Props applied to the wrapper element */
wrapperProps?: object;
/** Called before diff editor is mounted */
beforeMount?: DiffBeforeMount;
/** Called when diff editor is mounted */
onMount?: DiffOnMount;
}Usage Examples:
import React from "react";
import { DiffEditor } from "@monaco-editor/react";
// Basic diff comparison
function BasicDiffEditor() {
const originalCode = `function hello() {
console.log("Hello");
}`;
const modifiedCode = `function hello(name) {
console.log("Hello " + name);
}`;
return (
<DiffEditor
height="400px"
language="javascript"
original={originalCode}
modified={modifiedCode}
/>
);
}
// Advanced diff with custom options
function AdvancedDiffEditor() {
const original = `const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 }
];`;
const modified = `const users = [
{ name: "Alice", age: 25, active: true },
{ name: "Bob", age: 30, active: false },
{ name: "Charlie", age: 35, active: true }
];`;
const options = {
readOnly: false,
renderSideBySide: true,
enableSplitViewResizing: true,
renderOverviewRuler: true,
ignoreTrimWhitespace: false,
};
return (
<DiffEditor
height="500px"
language="javascript"
original={original}
modified={modified}
options={options}
theme="vs-dark"
/>
);
}
// Multi-language diff
function MultiLanguageDiff() {
const tsCode = `interface User {
name: string;
age: number;
}`;
const jsCode = `/**
* @typedef {Object} User
* @property {string} name
* @property {number} age
*/`;
return (
<DiffEditor
height="300px"
originalLanguage="typescript"
modifiedLanguage="javascript"
original={tsCode}
modified={jsCode}
/>
);
}Event handlers for diff editor lifecycle management.
/**
* Called when diff editor is mounted
* @param editor - The standalone diff editor instance
* @param monaco - The monaco editor namespace
*/
type DiffOnMount = (editor: editor.IStandaloneDiffEditor, monaco: Monaco) => void;
/**
* Called before diff editor is mounted
* @param monaco - The monaco editor namespace
*/
type DiffBeforeMount = (monaco: Monaco) => void;
type MonacoDiffEditor = editor.IStandaloneDiffEditor;Event Handler Examples:
import { DiffEditor } from "@monaco-editor/react";
function DiffWithHandlers() {
const handleBeforeMount = (monaco) => {
// Configure monaco before diff editor creation
monaco.editor.defineTheme('diffTheme', {
base: 'vs',
inherit: true,
rules: [],
colors: {
'diffEditor.insertedTextBackground': '#90EE9050',
'diffEditor.removedTextBackground': '#FF634750',
}
});
};
const handleMount = (diffEditor, monaco) => {
// Access both original and modified editors
const originalEditor = diffEditor.getOriginalEditor();
const modifiedEditor = diffEditor.getModifiedEditor();
// Configure diff editor behavior
diffEditor.updateOptions({
renderSideBySide: true,
enableSplitViewResizing: true,
});
// Focus on modified editor
modifiedEditor.focus();
// Add navigation commands
diffEditor.addCommand(monaco.KeyMod.Alt | monaco.KeyCode.DownArrow, () => {
diffEditor.goToNextDiff();
});
diffEditor.addCommand(monaco.KeyMod.Alt | monaco.KeyCode.UpArrow, () => {
diffEditor.goToPreviousDiff();
});
};
return (
<DiffEditor
height="400px"
language="javascript"
original="const a = 1;"
modified="const a = 2;"
beforeMount={handleBeforeMount}
onMount={handleMount}
theme="diffTheme"
/>
);
}Configuration options specific to the diff editor functionality.
// Diff editor specific options
interface DiffEditorOptions {
/** Monaco diff editor construction options */
options?: editor.IDiffEditorConstructionOptions;
}
// Content configuration for diff comparison
interface DiffContentProps {
/** Original (left) content */
original?: string;
/** Modified (right) content */
modified?: string;
/** Language for both sides */
language?: string;
/** Separate language for original side */
originalLanguage?: string;
/** Separate language for modified side */
modifiedLanguage?: string;
}
// Model paths for diff editor
interface DiffModelProps {
/** Path/URI for original model */
originalModelPath?: string;
/** Path/URI for modified model */
modifiedModelPath?: string;
/** Keep original model when unmounting */
keepCurrentOriginalModel?: boolean;
/** Keep modified model when unmounting */
keepCurrentModifiedModel?: boolean;
}Configuration Examples:
import { DiffEditor } from "@monaco-editor/react";
// Customized diff editor
function CustomDiffEditor() {
const diffOptions = {
// Display options
renderSideBySide: true, // Side-by-side vs inline
renderMarginRevertIcon: true, // Show revert icons
renderOverviewRuler: true, // Show overview ruler
renderIndicators: true, // Show change indicators
// Interaction options
enableSplitViewResizing: true, // Allow resizing split
readOnly: false, // Allow editing
originalEditable: false, // Original side read-only
// Diff algorithm options
ignoreTrimWhitespace: true, // Ignore whitespace at line ends
renderWhitespace: 'selection', // Show whitespace
diffWordWrap: 'inherit', // Word wrapping
// Navigation
automaticLayout: true, // Auto-resize
scrollBeyondLastLine: false,
};
return (
<DiffEditor
height="600px"
width="100%"
language="typescript"
theme="vs-dark"
options={diffOptions}
original="// Original code"
modified="// Modified code"
/>
);
}
// File-based diff with model paths
function FileDiffEditor() {
return (
<DiffEditor
height="500px"
language="javascript"
originalModelPath="file:///original/main.js"
modifiedModelPath="file:///modified/main.js"
original={`function calculate(a, b) {
return a + b;
}`}
modified={`function calculate(a, b) {
// Added validation
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('Arguments must be numbers');
}
return a + b;
}`}
keepCurrentOriginalModel={true}
keepCurrentModifiedModel={true}
/>
);
}import React, { useState } from "react";
import { DiffEditor } from "@monaco-editor/react";
function CodeReviewDiff() {
const [currentFile, setCurrentFile] = useState(0);
const changedFiles = [
{
filename: "src/utils.js",
original: `export function formatDate(date) {
return date.toLocaleDateString();
}`,
modified: `export function formatDate(date) {
if (!date || !(date instanceof Date)) {
throw new Error('Invalid date provided');
}
return date.toLocaleDateString();
}`
},
{
filename: "src/api.js",
original: `async function fetchUser(id) {
const response = await fetch(\`/api/users/\${id}\`);
return response.json();
}`,
modified: `async function fetchUser(id) {
const response = await fetch(\`/api/users/\${id}\`);
if (!response.ok) {
throw new Error(\`Failed to fetch user: \${response.status}\`);
}
return response.json();
}`
}
];
return (
<div>
<div style={{ marginBottom: 10 }}>
{changedFiles.map((file, index) => (
<button
key={index}
onClick={() => setCurrentFile(index)}
style={{
marginRight: 10,
fontWeight: currentFile === index ? 'bold' : 'normal',
background: currentFile === index ? '#007acc' : '#f0f0f0',
color: currentFile === index ? 'white' : 'black',
border: 'none',
padding: '8px 12px',
cursor: 'pointer'
}}
>
{file.filename}
</button>
))}
</div>
<DiffEditor
height="500px"
language="javascript"
original={changedFiles[currentFile].original}
modified={changedFiles[currentFile].modified}
options={{
renderSideBySide: true,
renderMarginRevertIcon: false,
readOnly: true,
}}
/>
</div>
);
}function VersionComparisonDiff() {
const [versions] = useState([
{ version: "1.0.0", code: "const API_URL = 'http://localhost';" },
{ version: "1.1.0", code: "const API_URL = process.env.API_URL || 'http://localhost';" },
{ version: "2.0.0", code: "const API_URL = process.env.REACT_APP_API_URL || 'https://api.example.com';" }
]);
const [leftVersion, setLeftVersion] = useState(0);
const [rightVersion, setRightVersion] = useState(1);
return (
<div>
<div style={{ marginBottom: 10 }}>
<label>Original:
<select value={leftVersion} onChange={(e) => setLeftVersion(+e.target.value)}>
{versions.map((v, i) => (
<option key={i} value={i}>{v.version}</option>
))}
</select>
</label>
<label style={{ marginLeft: 20 }}>Modified:
<select value={rightVersion} onChange={(e) => setRightVersion(+e.target.value)}>
{versions.map((v, i) => (
<option key={i} value={i}>{v.version}</option>
))}
</select>
</label>
</div>
<DiffEditor
height="300px"
language="javascript"
original={versions[leftVersion].code}
modified={versions[rightVersion].code}
/>
</div>
);
}