Monaco Editor for React Applications
react-monaco-editor provides a React component wrapper for the Monaco Editor, which is the code editor that powers VS Code. Currently stable at version 0.59.0, the package generally follows the updates of its `monaco-editor` peer dependency, releasing updates to ensure compatibility and add minor features or fixes. Its release cadence is moderate, with several patch and minor versions released per quarter, often driven by dependency updates or small feature enhancements. This library simplifies the integration of a powerful, feature-rich code editor into React applications, abstracting away much of the complex setup required by the standalone Monaco Editor, particularly concerning Webpack configuration and instance management. It exposes a declarative API through props, making it straightforward to embed a full-featured code editing experience.
Common errors
-
Error: Cannot find module 'monaco-editor'
cause The `monaco-editor` package, a peer dependency, is not installed or not resolvable by your build system.fixInstall `monaco-editor` explicitly: `npm install monaco-editor` or `yarn add monaco-editor`. If using an older version of `react-monaco-editor` (e.g., <0.10.0), this was a known issue, fixed by upgrading. [cite: `0.10.0` release notes] -
Module not found: Error: Can't resolve 'monaco-editor-webpack-plugin'
cause The `monaco-editor-webpack-plugin` is required for Webpack-based projects but has not been installed or correctly configured.fixInstall the plugin: `npm install monaco-editor-webpack-plugin --save-dev` or `yarn add monaco-editor-webpack-plugin --dev`. Then, add it to your `webpack.config.js` plugins array. -
ModuleParseError: Module was not found: 'monaco-editor/esm/vs/editor/editor.main.css'
cause Your Webpack configuration is not correctly handling CSS imports from the `monaco-editor` package, often due to conflicts with CSS Modules or incorrect loader setup.fixConfigure separate CSS loader rules in `webpack.config.js`. Apply `style-loader` and `css-loader` without `modules: true` specifically for files within `node_modules/monaco-editor`, while maintaining CSS Modules for your application's own CSS. -
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
cause This error often indicates that multiple instances of React are loaded in your application, which can happen with incompatible peer dependencies or misconfigured bundlers.fixVerify that you only have one version of React installed (`npm ls react` or `yarn why react`). Ensure `react` and `react-dom` peer dependencies of `react-monaco-editor` are satisfied by a single, compatible version in your project. This might involve resolving dependency tree conflicts or adjusting bundler configurations.
Warnings
- breaking Version 0.9.0 introduced breaking changes related to the underlying Monaco Editor upgrade and the `theme` option. Users upgrading from versions prior to 0.9.0 may need to adjust their theme configurations.
- gotcha Integrating `react-monaco-editor` with Webpack requires the `monaco-editor-webpack-plugin` (a separate package) and often specific CSS loader configurations to handle Monaco Editor's internal CSS imports, especially when using CSS Modules in your project. Incorrect setup leads to missing styles, worker loading failures, or build errors.
- gotcha `react-monaco-editor` lists `monaco-editor` as a peer dependency. This means you must explicitly install `monaco-editor` in your project. Mismatching versions between `react-monaco-editor` and `monaco-editor` can lead to runtime issues or unexpected behavior due to API incompatibilities.
- breaking The library has peer dependencies on `React >=16.8.0 <20.0.0` and `react-dom >=16.8.0 <20.0.0`. Attempting to use `react-monaco-editor` with React v20 or newer will result in compatibility issues or runtime errors, particularly related to hooks or deprecated lifecycle methods.
- gotcha The `overrideServices` prop allows advanced customization of Monaco's internal services. However, this relies on Monaco's internal implementations, which are not part of its public API and `may change over time` without notice, potentially causing unexpected behavior in future Monaco Editor updates.
Install
-
npm install react-monaco-editor -
yarn add react-monaco-editor -
pnpm add react-monaco-editor
Imports
- MonacoEditor
import { MonacoEditor } from 'react-monaco-editor';import MonacoEditor from 'react-monaco-editor';
- MonacoWebpackPlugin
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); - MonacoEditorProps, EditorDidMount
import { MonacoEditorProps, EditorDidMount } from 'react-monaco-editor';import type { MonacoEditorProps } from 'react-monaco-editor'; import type { editor } from 'monaco-editor'; type EditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: typeof editor) => void;
Quickstart
import React from 'react';
import { createRoot } from "react-dom/client";
import MonacoEditor from 'react-monaco-editor';
interface AppState {
code: string;
}
class App extends React.Component<{}, AppState> {
constructor(props: {}) {
super(props);
this.state = {
code: `function helloWorld() {
console.log("Hello, Monaco!");
// You can customize language, theme, and options.
const now = new Date();
return \`Current time: \${now.toLocaleTimeString()}\`;
}`,
};
this.onChange = this.onChange.bind(this);
this.editorDidMount = this.editorDidMount.bind(this);
}
editorDidMount(editor: any, monaco: any) {
console.log('editorDidMount instance:', editor);
console.log('monaco global instance:', monaco);
editor.focus();
// Example: add a new command (Ctrl+Enter)
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
alert('Ctrl+Enter pressed!');
});
}
onChange(newValue: string, event: any) {
console.log('Editor content changed:', newValue);
this.setState({ code: newValue });
}
render() {
const { code } = this.state;
const options = {
selectOnLineNumbers: true,
automaticLayout: true, // Essential for responsive editors
scrollBeyondLastLine: false,
minimap: {
enabled: true,
},
fontSize: 14,
};
return (
<div style={{ height: 'calc(100vh - 40px)', width: '100%', padding: '20px' }}>
<h2>Interactive Code Editor (TypeScript)</h2>
<MonacoEditor
width="100%"
height="100%"
language="typescript"
theme="vs-dark"
value={code}
options={options}
onChange={this.onChange}
editorDidMount={this.editorDidMount}
/>
</div>
);
}
}
const container = document.getElementById('root');
if (container) {
const root = createRoot(container);
root.render(<App />);
} else {
console.error("Failed to find the root element to render the app.");
}