codemirror and monaco-editor are both powerful libraries for embedding code editing capabilities into web applications. codemirror (specifically version 6) is designed as a modular toolkit, allowing developers to build lightweight, custom editors by picking only the features they need. monaco-editor is the engine that powers VS Code, offering a full-featured, desktop-class editing experience out of the box with rich language intelligence. While codemirror focuses on flexibility and small bundle size, monaco-editor prioritizes feature completeness and integration with the Language Server Protocol ecosystem.
Both codemirror and monaco-editor solve the problem of editing code in the browser, but they approach it from opposite ends of the spectrum. codemirror is a toolkit for building editors, while monaco-editor is a pre-built IDE component. Let's compare how they handle initialization, styling, language support, and extensibility.
codemirror requires you to assemble the editor from parts.
// codemirror: Assemble your editor
import { EditorView, basicSetup } from "codemirror"
import { EditorState } from "@codemirror/state"
const view = new EditorView({
state: EditorState.create({
doc: "console.log('Hello')",
extensions: [basicSetup]
}),
parent: document.querySelector("#editor")
})
monaco-editor provides a complete instance immediately.
// monaco-editor: Create a full instance
import * as monaco from 'monaco-editor';
const editor = monaco.editor.create(document.getElementById('container'), {
value: "console.log('Hello')",
language: 'javascript',
minimap: { enabled: true }
});
codemirror treats styles as standard CSS.
/* codemirror: Standard CSS overrides */
.cm-editor {
background-color: #f5f5f5;
font-family: 'Custom Font', monospace;
}
.cm-line {
color: #333;
}
monaco-editor uses a theme API object.
// monaco-editor: Define a custom theme
monaco.editor.defineTheme('myTheme', {
base: 'vs',
inherit: true,
rules: [{ token: 'comment', foreground: 'ffa500' }],
colors: { 'editor.background': '#f5f5f5' }
});
monaco.editor.setTheme('myTheme');
codemirror uses a leaky abstraction for language modes.
@codemirror/lang-javascript).// codemirror: Add language and autocomplete
import { javascript } from "@codemirror/lang-javascript"
import { autocompletion } from "@codemirror/autocomplete"
extensions: [
javascript(),
autocompletion({
override: [
(context) => {
return { from: context.pos, options: [{ label: "console" }] }
}
]
})
]
monaco-editor brings VS Code intelligence out of the box.
// monaco-editor: Register completion provider
monaco.languages.registerCompletionItemProvider('javascript', {
provideCompletionItems: (model, position) => {
return {
suggestions: [{
label: 'console',
kind: monaco.languages.CompletionItemKind.Function,
insertText: 'console'
}]
};
}
});
codemirror relies on a functional extension system.
// codemirror: Custom extension
import { StateField } from "@codemirror/state"
const myField = StateField.define({
create: () => 0,
update: (value, tr) => value + 1
})
extensions: [myField]
monaco-editor exposes a broad imperative API.
onDidChangeModelContent.// monaco-editor: Listen to content changes
editor.onDidChangeModelContent((e) => {
console.log('Content changed', e.changes);
});
// Add a custom action
editor.addAction({
id: 'my-action',
label: 'My Action',
run: (ed) => { console.log('Action run'); }
});
codemirror keeps the main thread light.
// codemirror: Minimal bundle import
import { EditorView } from "codemirror"
// Only loads what is explicitly imported
monaco-editor offloads work to web workers.
// monaco-editor: Configure worker loading (Webpack example)
// Requires specific loader config to handle worker imports
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
self.MonacoEnvironment = {
getWorker: function () {
return new editorWorker();
}
};
While the architectures differ, both libraries aim to solve the same core problems with reliability.
// codemirror: Accessing document text
const doc = view.state.doc.toString();
// monaco-editor: Accessing document text
const model = editor.getModel();
const doc = model.getValue();
// codemirror: Using search extension
import { search } from "@codemirror/search"
extensions: [search()]
// monaco-editor: Triggering find widget
editor.trigger('source', 'actions.find');
// React wrapper concept for both
// useEffect(() => {
// const instance = createEditor(...);
// return () => instance.destroy();
// }, [])
// Both libraries manage focus internally
// ensuring tab navigation works within the editor area
| Feature | Shared by CodeMirror and Monaco |
|---|---|
| Core Function | š Code editing, highlighting |
| Framework Support | š React, Vue, Angular, Vanilla |
| Basic Features | š Search, Undo/Redo, Cursors |
| Accessibility | ā Keyboard nav, Screen readers |
| License | š Open Source (MIT / BSD-3) |
| Feature | codemirror | monaco-editor |
|---|---|---|
| Architecture | š§© Modular, assemble-your-own | š¦ Monolithic, batteries-included |
| Bundle Size | š Small, tree-shakable | š Large, requires worker config |
| Language Intel | š ļø Manual setup for completions | š§ VS Code IntelliSense built-in |
| Theming | šØ Standard CSS | šļø JS Theme API |
| Setup Complexity | š Low (for basic), High (for IDE) | š High (initial config) |
| Best Use Case | š Forms, Lightweight Inputs | š» Cloud IDEs, Complex Configs |
codemirror is like a set of high-quality Lego bricks š§± ā you build exactly what you need, nothing more. It shines in forms, documentation tools, or apps where the editor is just one part of a larger interface. The learning curve is steeper if you want IDE features, but the payoff is a tailored, performant component.
monaco-editor is like a pre-fabricated room š ā it comes with furniture, wiring, and paint already done. It is the clear winner for cloud IDEs, complex JSON/YAML editors, or any app where users expect VS Code behavior. The trade-off is weight and less flexibility over the internal UI.
Final Thought: If you are building a code editor product, start with monaco-editor. If you are building a product that contains code editing, start with codemirror.
Choose codemirror if you need a lightweight editor that integrates seamlessly into your existing design system without imposing a specific look and feel. It is ideal for scenarios where bundle size is critical, or when you need to build a specialized editing experience that differs significantly from a standard IDE. The modular architecture allows you to include only the features you actually use, keeping your application fast and lean.
Choose monaco-editor if your users expect a full IDE experience similar to VS Code, including advanced IntelliSense, refactoring tools, and complex language support. It is the best fit for cloud development environments, heavy-duty configuration editors, or applications where editing complex codebases is the primary task. Be prepared to handle a larger bundle size and the complexity of configuring web workers for language services.
[ WEBSITE | DOCS | ISSUES | FORUM | CHANGELOG ]
This package provides an example configuration for the
CodeMirror code editor. The actual editor
is implemented in the various packages under the @codemirror scope,
which this package depends on.
The project page has more information, a number of examples and the documentation.
This code is released under an MIT license.
We aim to be an inclusive, welcoming community. To make that explicit, we have a code of conduct that applies to communication around the project.
import {EditorView, basicSetup} from "codemirror"
const view = new EditorView({
parent: document.body,
doc: "Hello",
extensions: [basicSetup /* ... */]
})
This sets up a basic code editor containing the word "Hello". You'll usually want to add at least a language mode to your configuration.