mrkeyoor.com_
Sat 08 Aug 20:59 UTC
npmWeb Frontendupdated 08 Aug 2026

@monaco-editor/react

`@monaco-editor/react` is a React wrapper and loader for Monaco Editor, the browser code editor that powers VS Code. It mounts standard and diff editors, exposes both Monaco and editor instances through hooks and callbacks, manages models by path for tabbed interfaces, and hides most AMD loader configuration. The wrapper itself is small, but the editor it loads is a large browser application with workers, language services, models, commands, and its own lifecycle.

Verdict

Use it when the product genuinely needs Monaco's IDE behavior, especially diff views or multi-file models. For forms, snippets, and modest configuration editors, CodeMirror is usually lighter and easier to own.

API stability4/5The 4.x component contract has settled around `Editor`, `DiffEditor`, `loader`, `useMonaco`, lifecycle callbacks, model paths, and direct Monaco options. Version 4.7.0 supports a wide React peer range and still documents all main props. Most churn comes from Monaco itself, React rendering modes, and bundler worker setup rather than wrapper concepts, so pin both the wrapper and `monaco-editor` when self-hosting assets.
Docs5/5The README is unusually complete for a wrapper: installation, simple and controlled use, refs, lifecycle callbacks, three ways to obtain Monaco, asynchronous hook behavior, loader configuration, npm self-hosting, multi-model state, validation limits, Electron and Next.js notes, custom-editor composition, and full prop tables for both editors. The dedicated demo site gives an immediate behavior check, though a few ReactDOM examples retain older rendering style.
Maintenance4/5The latest registry version is 4.7.0, GitHub reported about 4,732 stars and 21 open issues and PRs, and the repository was pushed in April 2026. The broad React peer range through 19 and recent loader dependency indicate current compatibility work. Maintenance depends on tracking a complex upstream editor and browser bundlers, so seemingly small Monaco or framework upgrades still deserve production smoke tests.
Ecosystem5/5The wrapper exposes Monaco rather than hiding it, so Microsoft Monaco languages, workers, themes, markers, models, commands, completion providers, TypeScript defaults, and editor options remain usable. It works with common React generators and documents Electron and Next.js constraints. The tradeoff is that Monaco's AMD loader and web-worker model do not fit every bundler or CSP automatically; integrations inherit the editor's full deployment surface.

Use it if

  • You need a VS Code-like editor or diff view inside a React application
  • You want Monaco without ejecting or hand-configuring a typical Vite, Next.js, or generated React build
  • You need multi-file models that preserve undo stacks, selections, scroll positions, and view state by path
  • You need direct access to Monaco language services, markers, themes, commands, and editor instances
Skip it if

Setup reality

Install `@monaco-editor/react` with React and React DOM. Version 4.7.0 declares React 16.8 through 19 and `monaco-editor >=0.25 <1` as peers; the README notes that TypeScript definitions depend on `monaco-editor`, so install it explicitly even when the default CDN supplies runtime files. Out of the box, the loader downloads Monaco's AMD assets from a CDN at first mount. That means the first editor can show a loading state, offline use fails, and CSP, Electron, air-gapped, or privacy-sensitive deployments need `loader.config(...)` before any editor or `useMonaco()` call initializes the singleton. Self-hosting with the npm Monaco package can require worker and bundler configuration, and the README warns that some generated setups may need plugins or ejection. Next.js must mount the editor on the client because Monaco touches `document`. `useMonaco()` returns null on its first render because initialization is asynchronous. Choose controlled `value` versus one-time `defaultValue` intentionally. For multi-file editing, stable URI-like `path` values identify models; default props only apply when a model is created, and retained models consume memory until disposed. `onValidate` is not a universal linter, and large files or too many live models need editor options and lifecycle limits. Keep secrets out of editor text and callbacks because models are in browser memory.

Patterns

Render a basic code editorrender-editor

import Editor from '@monaco-editor/react';

export function CodeField() {
  return (
    <Editor
      height="60vh"
      defaultLanguage="typescript"
      defaultValue="const answer: number = 42;"
    />
  );
}

`defaultValue` and `defaultLanguage` apply when the model is created; use controlled props for later external updates.

Use a controlled editor valuecontrol-editor-value

const [code, setCode] = useState('');

<Editor
  language="json"
  value={code}
  onChange={(value) => setCode(value ?? '')}
/>;

`onChange` can receive `undefined`; normalize it before storing a string state.

Keep the editor instance in a refaccess-editor-instance

const editorRef = useRef(null);

<Editor onMount={(editor) => { editorRef.current = editor; }} />;

function formatDocument() {
  editorRef.current?.getAction('editor.action.formatDocument')?.run();
}

Do not put the editor instance in React state; it is mutable and should not trigger rendering.

Configure TypeScript before mountconfigure-typescript

<Editor
  defaultLanguage="typescript"
  beforeMount={(monaco) => {
    monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
      strict: true,
      target: monaco.languages.typescript.ScriptTarget.ES2022,
    });
  }}
/>;

Use `beforeMount` for language-service defaults so they are ready before the first model is shown.

Use Monaco outside the editor callbackuse-monaco-hook

const monaco = useMonaco();

useEffect(() => {
  if (!monaco) return;
  monaco.editor.defineTheme('app-dark', {
    base: 'vs-dark',
    inherit: true,
    rules: [],
    colors: {},
  });
}, [monaco]);

The hook initially returns null because the shared loader initializes asynchronously.

Use the installed Monaco package instead of the CDNself-host-monaco

import * as monaco from 'monaco-editor';
import { loader } from '@monaco-editor/react';

loader.config({ monaco });

Run this before any editor or hook initializes; your bundler must also package Monaco workers and assets correctly.

Compare two documentsrender-diff-editor

import { DiffEditor } from '@monaco-editor/react';

<DiffEditor
  height="70vh"
  language="json"
  original={beforeJson}
  modified={afterJson}
  options={{ readOnly: true, renderSideBySide: true }}
/>;

Provide stable model paths when the diff documents need persistent model identity across renders.

Switch models by file pathbuild-multi-file-editor

<Editor
  path={`file:///${activeFile.name}`}
  defaultLanguage={activeFile.language}
  defaultValue={activeFile.contents}
  saveViewState
/>;

A stable `path` preserves each model's undo stack, selection, and scroll state; default props are ignored when that model already exists.

Read language-service validation markersobserve-validation-markers

<Editor
  defaultLanguage="json"
  onValidate={(markers) => {
    setErrors(markers.map((m) => ({
      message: m.message,
      line: m.startLineNumber,
    })));
  }}
/>;

The README says this fires with rich validation languages, not every language that Monaco can colorize.

Tune an embedded read-only viewerconfigure-editor-options

<Editor
  value={code}
  language="javascript"
  options={{
    readOnly: true,
    minimap: { enabled: false },
    lineNumbers: 'off',
    scrollBeyondLastLine: false,
    automaticLayout: true,
  }}
/>;

`automaticLayout` is convenient in responsive containers but adds resize observation work.

Register a custom completion provideradd-completion-provider

function beforeMount(monaco) {
  monaco.languages.registerCompletionItemProvider('yaml', {
    provideCompletionItems: () => ({
      suggestions: [{
        label: 'service',
        kind: monaco.languages.CompletionItemKind.Keyword,
        insertText: 'service: ${1:name}',
        insertTextRules:
          monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
      }],
    }),
  });
}

<Editor beforeMount={beforeMount} defaultLanguage="yaml" />;

Registration returns a disposable; keep and dispose it if the provider is scoped to a component lifetime.

Keep a model across component unmountspreserve-model-on-unmount

<Editor
  path="file:///draft.ts"
  keepCurrentModel
  saveViewState
/>;

Retained models consume memory; dispose them explicitly with Monaco when the document is truly closed.

Alternatives

PackageRegistryPick it when
@uiw/react-codemirrornpmYou want a lighter React editor assembled from CodeMirror 6 extensions
react-acenpmYou already use Ace modes and themes and need a mature React wrapper around them
@codemirror/viewnpmYou can build your own React integration and want direct control over a modular editor core