Jesse A. Green / notebook

Monaco Editor — User Guide

Generated June 2026 from microsoft/monaco-editor at 4cd0f4f

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Monaco Editor is the browser-based code editor that powers Visual Studio Code. The monaco-editor package is the distributable build of that editor: it bundles the editor core, syntax highlighting for ~90 languages, rich language services for TypeScript/JavaScript, CSS/SCSS/LESS, HTML, and JSON, plus the tooling you need to embed a full-featured code editor in any web page.

This guide is for web developers who want to embed Monaco in an application — a playground, an online IDE, a configuration editor, a documentation site, or anything that needs real code editing in the browser. It covers installation, the all-important web worker setup, creating and configuring editors, language support, themes, commands, and integration with every major bundler.

Note

The actual editor engine ships as a sibling package called monaco-editor-core, which Microsoft builds from the VS Code source tree at a pinned commit. The monaco-editor package wraps that core, adds the language definitions and language services, and is the package you install. Everywhere this guide says "Monaco", it means the editor you get from npm install monaco-editor.


1. Quick Start

The fastest way to see Monaco running is the prebuilt AMD bundle served from a CDN. Paste this into an HTML file and open it in a browser — no build step required.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <style>
      html, body, #container { height: 100%; margin: 0; }
    </style>
  </head>
  <body>
    <div id="container"></div>

    <script src="https://cdn.jsdelivr.net/npm/monaco-editor/min/vs/loader.js"></script>
    <script>
      require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor/min/vs' } });
      require(['vs/editor/editor.main'], function () {
        monaco.editor.create(document.getElementById('container'), {
          value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
          language: 'javascript'
        });
      });
    </script>
  </body>
</html>
A code editor with JavaScript syntax highlighting, a minimap, line numbers,
IntelliSense, and the full VS Code editing experience appears in #container.

Tip

The CDN/AMD path is ideal for prototypes, demos, and plain HTML pages because the loader resolves the editor and its web workers for you. For production applications built with a bundler (webpack, Vite, esbuild, Parcel), use the ESM installation in the next section so the editor is part of your dependency graph and your assets are versioned.

The same hello-world with an ESM bundler:

import * as monaco from 'monaco-editor';

monaco.editor.create(document.getElementById('container'), {
  value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
  language: 'javascript'
});

Important

With a bundler you must tell Monaco how to load its web workers, or language features will fail at runtime. See Section 3: Web Workers before shipping. The CDN/AMD path handles this automatically; the ESM path does not.


2. Installation

2.1 Install from npm

npm install monaco-editor
added 1 package

This installs the package whose package.json declares:

Field Value Meaning
version 0.55.1 The release you are using.
main ./min/vs/editor/editor.main.js Minified AMD build (CommonJS require).
module ./esm/vs/editor/editor.main.js ES module build (bundlers/import).
typings ./esm/vs/editor/editor.main.d.ts TypeScript declarations.

The exports map also exposes the ESM tree under subpaths, so deep imports such as monaco-editor/esm/vs/editor/editor.api and the per-language worker files resolve correctly.

2.2 What's in the package

After install, the relevant directories are:

Path Contents
monaco-editor/esm/ ES module build — use this with bundlers.
monaco-editor/min/ Minified AMD build — use this with the CDN/AMD loader.
monaco-editor/min/vs/loader.js The AMD loader for the CDN/AMD path.
monaco-editor/esm/vs/editor/editor.main.js Full editor with all languages and features.
monaco-editor/esm/vs/editor/editor.api.js Editor API only — you opt into languages/features yourself.
monaco-editor/esm/vs/editor/editor.worker.js The core editor web worker.
monaco-editor/esm/vs/language/{json,css,html,typescript}/*.worker.js Language-service web workers.

Note

editor.main pulls in everything (all languages + all editor features) and is the simplest starting point. editor.api pulls in only the editor and lets you import exactly the languages and features you want for a smaller bundle. Most applications start with editor.main and optimize later (see Section 11).

2.3 Three ways to import

Depending on your setup, you will import Monaco one of these ways:

Bare specifier (bundler resolves it)

import * as monaco from 'monaco-editor';

Explicit ESM entry

import * as monaco from 'monaco-editor/esm/vs/editor/editor.main.js';

API-only (opt into languages/features)

import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';

3. Web Workers — the most important setup step

Monaco runs its expensive language work (TypeScript type-checking, JSON schema validation, CSS/HTML analysis, and the core editor's text-diffing/tokenization) inside web workers so the UI thread stays responsive. With a bundler, Monaco cannot guess the URLs of those worker scripts — you must provide them via the global MonacoEnvironment object.

Caution

If you skip this step, you will see runtime errors such as You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker, or language features (autocomplete, validation, formatting) will silently do nothing. This is the single most common Monaco integration problem.

3.1 The two hooks

MonacoEnvironment accepts either of two functions. Define one before you create any editor:

Hook Returns Use when
getWorker(moduleId, label) a Worker (or Promise<Worker>) Your bundler can produce Worker instances directly (Vite, modern webpack 5, esbuild with a worker plugin). Preferred.
getWorkerUrl(moduleId, label) a URL string to the worker script Your bundler emits worker files to known URLs and you load them by path.

The label argument tells you which worker is needed. Map each label to the matching worker bundle:

label value Worker to return
json json.worker
css, scss, less css.worker
html, handlebars, razor html.worker
typescript, javascript ts.worker
anything else (default) editor.worker

3.2 getWorker with Vite

Vite resolves the ?worker suffix to a worker constructor, which is the cleanest approach:

import * as monaco from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';

self.MonacoEnvironment = {
  getWorker(_, label) {
    if (label === 'json') return new jsonWorker();
    if (label === 'css' || label === 'scss' || label === 'less') return new cssWorker();
    if (label === 'html' || label === 'handlebars' || label === 'razor') return new htmlWorker();
    if (label === 'typescript' || label === 'javascript') return new tsWorker();
    return new editorWorker();
  }
};

3.3 getWorkerUrl with webpack (manual)

If you wire workers by URL, return the path your bundler emits the worker to:

import * as monaco from 'monaco-editor';

self.MonacoEnvironment = {
  getWorkerUrl: function (moduleId, label) {
    if (label === 'json') return './json.worker.bundle.js';
    if (label === 'css' || label === 'scss' || label === 'less') return './css.worker.bundle.js';
    if (label === 'html' || label === 'handlebars' || label === 'razor') return './html.worker.bundle.js';
    if (label === 'typescript' || label === 'javascript') return './ts.worker.bundle.js';
    return './editor.worker.bundle.js';
  }
};

Tip

For webpack, you usually do not write this by hand — the official monaco-editor-webpack-plugin generates the MonacoEnvironment and emits the worker bundles for you. See Section 10.1.

3.4 getWorkerUrl with esbuild

esbuild emits the worker files to a known output folder, so you return their relative URLs:

import * as monaco from 'monaco-editor/esm/vs/editor/editor.main.js';

self.MonacoEnvironment = {
  getWorkerUrl: function (moduleId, label) {
    if (label === 'json') return './vs/language/json/json.worker.js';
    if (label === 'css' || label === 'scss' || label === 'less') return './vs/language/css/css.worker.js';
    if (label === 'html' || label === 'handlebars' || label === 'razor') return './vs/language/html/html.worker.js';
    if (label === 'typescript' || label === 'javascript') return './vs/language/typescript/ts.worker.js';
    return './vs/editor/editor.worker.js';
  }
};

3.5 getWorkerUrl with Parcel

Parcel's url: import scheme gives you the emitted URL of each worker:

import JSONWorker from 'url:monaco-editor/esm/vs/language/json/json.worker.js';
import CSSWorker from 'url:monaco-editor/esm/vs/language/css/css.worker.js';
import HTMLWorker from 'url:monaco-editor/esm/vs/language/html/html.worker.js';
import TSWorker from 'url:monaco-editor/esm/vs/language/typescript/ts.worker.js';
import EditorWorker from 'url:monaco-editor/esm/vs/editor/editor.worker.js';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.main.js';

self.MonacoEnvironment = {
  getWorkerUrl: function (moduleId, label) {
    if (label === 'json') return JSONWorker;
    if (label === 'css' || label === 'scss' || label === 'less') return CSSWorker;
    if (label === 'html' || label === 'handlebars' || label === 'razor') return HTMLWorker;
    if (label === 'typescript' || label === 'javascript') return TSWorker;
    return EditorWorker;
  }
};

Note

Returning only the editor worker (the default branch) is valid if you don't need any rich language services. A minimal setup that returns editor.worker for every label still gives you syntax highlighting via the basic-language tokenizers — you just lose TypeScript/JSON/CSS/HTML IntelliSense and validation.

3.6 Cross-origin workers

If your worker files are served from a different origin than the page (for example a CDN), browsers block constructing a Worker directly from the cross-origin URL. The standard workaround is to wrap the real worker in a same-origin proxy script created from a Blob:

self.MonacoEnvironment = {
  getWorkerUrl: function (moduleId, label) {
    const workerUrl = '/* …resolve to the cross-origin URL… */';
    const proxy = URL.createObjectURL(
      new Blob([`importScripts('${workerUrl}');`], { type: 'application/javascript' })
    );
    return proxy;
  }
};

Tip

The monaco-editor-webpack-plugin already generates this Blob-proxy logic for cross-origin worker URLs automatically. If you use the plugin, set its publicPath option and let it handle the cross-origin case.


4. Creating and disposing an editor

4.1 monaco.editor.create

monaco.editor.create(domElement, options) mounts an editor into a DOM element and returns an editor instance:

const editor = monaco.editor.create(document.getElementById('container'), {
  value: 'const foo = () => 0;',
  language: 'javascript',
  theme: 'vs-dark'
});
The editor fills #container and renders the code with dark-theme highlighting.

Important

The container element must have a height. Monaco sizes itself to its container; if the container collapses to 0px, the editor renders but is invisible. Give the container an explicit height (CSS height, flex, or grid) or enable automaticLayout (Section 5) so the editor re-measures on resize.

4.2 Disposing

Editors hold DOM nodes, listeners, and worker connections. When you remove an editor, dispose it to release them:

editor.dispose();

If you created models explicitly (Section 6), dispose those too:

const model = editor.getModel();
editor.setModel(null);
if (model) model.dispose();

Warning

Failing to dispose editors and models in single-page apps that mount/unmount editors repeatedly leads to leaked workers and detached DOM nodes. Always dispose on component teardown (e.g. React useEffect cleanup, Vue onUnmounted).


5. Editor options

You pass options when creating the editor and change them later with editor.updateOptions(...). These are the most commonly used options, all confirmed against the editor's own option surface:

Option Type Example Effect
value string 'hello' Initial text (when not using an explicit model).
language string 'typescript' Language id for syntax highlighting + services.
theme string 'vs-dark' Color theme (see Section 7).
model ITextModel | null a created model Use a specific model instead of value/language.
readOnly boolean true Disable editing.
lineNumbers 'on' | 'off' | 'relative' | fn 'on' Line-number gutter mode.
glyphMargin boolean true Show the glyph margin (for decorations/breakpoints).
minimap { enabled: boolean } { enabled: false } Toggle the minimap.
roundedSelection boolean false Rounded vs. square selection corners.
scrollBeyondLastLine boolean false Allow scrolling past the last line.
renderWhitespace 'none' | 'boundary' | 'all' 'all' Render whitespace characters.
wordWrap 'off' | 'on' | 'wordWrapColumn' | 'bounded' 'on' Soft wrapping.
folding boolean true Enable code folding.
automaticLayout boolean true Re-layout automatically when the container resizes.

Note

The full option set is large (hundreds of options covering cursor, scrollbar, suggestions, bracket pair colorization, sticky scroll, accessibility, and more). The above are the everyday options. For the exhaustive, version-accurate list, consult the API reference — see Section 14: Reference.

5.1 Changing options at runtime

editor.updateOptions({ lineNumbers: 'off' });
editor.updateOptions({ minimap: { enabled: true } });
editor.updateOptions({ wordWrap: 'on' });
editor.updateOptions({ readOnly: true });
Each call applies immediately without recreating the editor.

5.2 Reading and writing content

const text = editor.getValue();        // current text
editor.setValue('new content');        // replace all text
const position = editor.getPosition(); // { lineNumber, column }
editor.setScrollTop(0);                // scroll to top

6. Models — the text behind the editor

A model holds the text, language, and edit history for a document. The editor is a view onto a model. Working with models explicitly lets you share one document across editors, attach a file URI (which TypeScript and JSON services use to resolve modules and schemas), and manage multiple open documents.

6.1 Creating a model

const model = monaco.editor.createModel(
  'class Test {}',
  'typescript',
  monaco.Uri.parse('file:///main.ts')
);

const editor = monaco.editor.create(document.getElementById('root'), {
  model: model,
  theme: 'vs-dark'
});

Tip

The third argument — the model URI — matters for the TypeScript/JavaScript language service. Giving models real file:/// URIs lets the service treat them as a project, resolve imports between them, and report cross-file diagnostics.

6.2 Switching models

const oldModel = editor.getModel();
const newModel = monaco.editor.createModel(source, 'python');
editor.setModel(newModel);
if (oldModel) oldModel.dispose();

6.3 Changing a model's language

monaco.editor.setModelLanguage(model, 'json');

6.4 Reacting to edits

model.onDidChangeContent((event) => {
  console.log('document changed', model.getValue());
});

7. Themes

Monaco ships four built-in themes:

Theme id Description
vs Light (default).
vs-dark Dark.
hc-black High-contrast dark.
hc-light High-contrast light.

Set the theme at creation time (theme: option) or switch it globally at any time:

monaco.editor.setTheme('vs');
monaco.editor.setTheme('vs-dark');
monaco.editor.setTheme('hc-black');

Important

monaco.editor.setTheme is global — it changes the theme for every editor on the page, not just one instance.

7.1 Custom themes

Define your own theme with token color rules and editor colors, then apply it by name:

monaco.editor.defineTheme('myTheme', {
  base: 'vs-dark',
  inherit: true,
  rules: [
    { token: 'comment', foreground: '6A9955', fontStyle: 'italic' },
    { token: 'keyword', foreground: 'C586C0' }
  ],
  colors: {
    'editor.background': '#1E1E1E'
  }
});

monaco.editor.setTheme('myTheme');

8. Language support

Monaco's language support comes in two tiers.

8.1 Basic languages (syntax highlighting)

The monaco-editor package bundles Monarch tokenizer definitions that provide syntax highlighting, bracket matching, comment toggling, and auto-indentation for a large catalog of languages. When you import editor.main, every basic language is registered automatically. The full set bundled in this release:

abap, apex, azcli, bat, bicep, cameligo, clojure, coffee, cpp, csharp, csp,
css, cypher, dart, dockerfile, ecl, elixir, flow9, freemarker2, fsharp, go,
graphql, handlebars, hcl, html, ini, java, javascript, julia, kotlin, less,
lexon, liquid, lua, m3, markdown, mdx, mips, msdax, mysql, objective-c,
pascal, pascaligo, perl, pgsql, php, pla, postiats, powerquery, powershell,
protobuf, pug, python, qsharp, r, razor, redis, redshift, restructuredtext,
ruby, rust, sb, scala, scheme, scss, shell, solidity, sophia, sparql, sql,
st, swift, systemverilog, tcl, twig, typescript, typespec, vb, wgsl, xml, yaml

Note

Basic-language support is highlighting only — it does not require a web worker and does not provide IntelliSense, validation, or formatting. For those, you need a rich language service (next section).

List the registered languages at runtime:

monaco.languages.getLanguages().forEach((language) => {
  console.log(language.id);
});

Register a language id (for example to attach your own tokenizer):

monaco.languages.register({ id: 'mylang' });

8.2 Rich language services

Four languages get full language services running in web workers: TypeScript/JavaScript, JSON, CSS/SCSS/LESS, and HTML/Handlebars/Razor. These provide completions, hover, diagnostics, formatting, and more. They are exposed under monaco.languages.<name> and are configured through a *Defaults object.

Caution

Rich language services require the matching web worker (Section 3). If you only wired up editor.worker, TypeScript completions and JSON validation will not appear.

TypeScript / JavaScript

monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
  target: monaco.languages.typescript.ScriptTarget.ES2020,
  module: monaco.languages.typescript.ModuleKind.ESNext,
  strict: true
});

monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
  noSemanticValidation: false,
  noSyntaxValidation: false
});

monaco.languages.typescript.typescriptDefaults.setInlayHintsOptions({
  includeInlayParameterNameHints: 'all',
  includeInlayFunctionParameterTypeHints: true,
  includeInlayVariableTypeHints: true,
  includeInlayFunctionLikeReturnTypeHints: true,
  includeInlayEnumMemberValueHints: true
});

Add extra type declarations (e.g. library .d.ts) so completions know about your APIs:

monaco.languages.typescript.typescriptDefaults.addExtraLib(
  'declare function greet(name: string): void;',
  'file:///globals.d.ts'
);

Tip

Use javascriptDefaults (instead of typescriptDefaults) to configure the service for plain JavaScript models. Both share the same option shapes.

JSON

monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
  validate: true,
  schemas: [
    {
      uri: 'http://myserver/schema.json',
      fileMatch: ['*'],
      schema: {
        type: 'object',
        properties: { name: { type: 'string' } },
        required: ['name']
      }
    }
  ]
});
Editing a JSON model now shows live validation errors and schema-driven completions.

CSS / SCSS / LESS and HTML

monaco.languages.css.cssDefaults.setOptions({
  validate: true,
  lint: { emptyRules: 'warning' }
});

monaco.languages.html.htmlDefaults.setOptions({
  format: { wrapLineLength: 120 }
});

Note

CSS support covers three languages handled by one worker (css, scss, less); HTML support covers html, handlebars, and razor. The defaults objects are cssDefaults/scssDefaults/lessDefaults and htmlDefaults/handlebarDefaults/razorDefaults respectively.

8.3 Extending a language yourself

For languages without a rich service, you can register your own providers — completions, hovers, definitions, formatting, and so on:

monaco.languages.registerCompletionItemProvider('mylang', {
  provideCompletionItems: () => ({
    suggestions: [
      {
        label: 'hello',
        kind: monaco.languages.CompletionItemKind.Keyword,
        insertText: 'hello'
      }
    ]
  })
});

Tip

The official Monaco Playground (linked in Section 14) has runnable, copy-paste examples for every provider type — completion, hover, signature help, code lens, color, folding, semantic tokens, inlay hints, symbols, and custom Monarch languages. It is the best place to learn the provider APIs.


9. Commands, actions, and keybindings

9.1 Add a command

A command binds a keyboard shortcut to a handler:

editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.F9, function () {
  alert('Command Running!!');
});

9.2 Add an action

An action is a richer command that also appears in the command palette and (optionally) the right-click context menu:

editor.addAction({
  id: 'my-unique-id',
  label: 'My Label!!!',
  keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F10],
  contextMenuGroupId: 'navigation',
  contextMenuOrder: 2.5,
  run: function (ed) {
    console.log("i'm running => " + ed.getPosition());
  }
});
The action is now reachable via Ctrl/Cmd+F10, the F1 command palette
("My Label!!!"), and the editor's right-click context menu.

9.3 Building keybindings

Combine modifiers and keys with bitwise OR using monaco.KeyMod and monaco.KeyCode:

Constant Example
monaco.KeyMod.CtrlCmd Ctrl on Windows/Linux, Cmd on macOS
monaco.KeyMod.Shift Shift
monaco.KeyMod.Alt Alt/Option
monaco.KeyMod.WinCtrl Ctrl on macOS, Win on Windows
monaco.KeyCode.KeyS, monaco.KeyCode.F9, … Letter and function keys
// Ctrl/Cmd + Shift + S
const chord = monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyS;

10. Bundler integration recipes

Monaco ships sample projects for every major bundler. The patterns below are distilled from those samples.

10.1 Webpack — monaco-editor-webpack-plugin

The official plugin generates the MonacoEnvironment worker wiring and emits the worker bundles, so you don't hand-write Section 3:

npm install monaco-editor monaco-editor-webpack-plugin
// webpack.config.js
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');

module.exports = {
  module: {
    rules: [
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
      { test: /\.ttf$/, type: 'asset/resource' }
    ]
  },
  plugins: [new MonacoWebpackPlugin()]
};
// app.js
import * as monaco from 'monaco-editor';
monaco.editor.create(document.getElementById('container'), {
  value: 'hello world',
  language: 'plaintext'
});

Restrict the bundle to only the languages and features you use:

new MonacoWebpackPlugin({
  languages: ['javascript', 'typescript', 'json'],
  features: ['!gotoSymbol']   // include all features except gotoSymbol
});

Tip

In the plugin's features list, prefix a feature with ! to exclude it while keeping all others. The plugin's full option set (languages, customLanguages, features, filename, monacoEditorPath, publicPath, globalAPI) is documented in the Admin Guide.

10.2 Vite

Use the getWorker setup from Section 3.2. No plugin is required. A typical React component:

import { useEffect, useRef } from 'react';
import * as monaco from 'monaco-editor';
import './userWorker'; // file that sets self.MonacoEnvironment (Section 3.2)

export function Editor() {
  const ref = useRef<HTMLDivElement>(null);
  useEffect(() => {
    const editor = monaco.editor.create(ref.current!, {
      value: 'console.log("hello")',
      language: 'javascript',
      theme: 'vs-dark'
    });
    return () => editor.dispose();
  }, []);
  return <div ref={ref} style={{ height: '100%' }} />;
}

10.3 esbuild

Use the getWorkerUrl setup from Section 3.4 and copy the worker files into your output directory as part of the build. The bundled sample uses a small build.js that runs esbuild and stages the vs/ worker tree alongside the app.

10.4 Parcel

Use the url: worker imports from Section 3.5. Parcel resolves and emits each worker automatically.

10.5 Electron

Electron renderer processes are browser contexts, so the standard ESM + worker setup applies. Load the bundled page into a BrowserWindow:

const { app, BrowserWindow } = require('electron');

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: { worldSafeExecuteJavaScript: true }
  });
  win.loadURL(`file://${__dirname}/dist/index.html`);
}

app.on('ready', createWindow);

Note

The repository also contains legacy AMD samples for Electron and NW.js under samples/legacy/. Those are no longer supported and are slated for removal — prefer the ESM samples for any new project.


11. Shrinking your bundle

The full editor.main import includes every language and editor feature. To trim it:

  • Use the webpack plugin's languages/features options (Section 10.1) — the easiest path for webpack users.
  • Import editor.api and opt in manually. Start from monaco-editor/esm/vs/editor/editor.api.js, then import only the feature and language contributions you need. The bundled browser-esm-webpack-small sample shows the pattern: a commented list of every available feature import (e.g. .../contrib/find/browser/findController.js) and language contribution (e.g. .../basic-languages/python/python.contribution.js) that you uncomment selectively.
// Opt-in example: editor + find + Python highlighting only
import 'monaco-editor/esm/vs/editor/browser/controller/coreCommands.js';
import 'monaco-editor/esm/vs/editor/contrib/find/browser/findController.js';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
import 'monaco-editor/esm/vs/basic-languages/python/python.contribution.js';

self.MonacoEnvironment = {
  getWorkerUrl: () => './editor.worker.bundle.js'
};

monaco.editor.create(document.getElementById('container'), {
  value: 'print("hello")',
  language: 'python'
});

Tip

Opting in manually is the most effective way to reduce bundle size, but it is also the most error-prone — miss a feature import and that feature silently disappears. Start with editor.main, ship, then optimize with measurements.


12. Diff editor

To compare two documents side by side, create a diff editor with an original and a modified model:

const original = monaco.editor.createModel('line one\nline two', 'text/plain');
const modified = monaco.editor.createModel('line one\nline 2', 'text/plain');

const diffEditor = monaco.editor.createDiffEditor(document.getElementById('container'), {
  theme: 'vs-dark',
  renderSideBySide: true
});

diffEditor.setModel({ original, modified });
A two-pane diff highlighting the change from "line two" to "line 2".

Note

Set renderSideBySide: false for an inline (unified) diff. Dispose the diff editor and both models when you're done.


13. Troubleshooting

Error: You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker

  • Cause: No web worker provider was configured, and a feature tried to start a worker.
  • Solution: Set self.MonacoEnvironment with either getWorker or getWorkerUrl before creating any editor (Section 3).
  • Prevention: Put the worker setup in a module that is imported first, or use monaco-editor-webpack-plugin so it's generated for you.

Language features (autocomplete/validation) do nothing

  • Cause: Only the core editor.worker is wired; the language-specific worker (e.g. ts.worker, json.worker) is missing.
  • Solution: Map each label (json, css, html, typescript/javascript) to its worker in getWorker/getWorkerUrl (Section 3.1).
  • Prevention: Use the full mapping from the samples, even if you think you only need one language.

The editor is invisible / zero height

  • Cause: The container element has no height.
  • Solution: Give the container an explicit height, or set automaticLayout: true so the editor re-measures.
  • Prevention: Style the container (height: 100% within a sized parent) before creating the editor.

Editor doesn't resize when its container changes

  • Cause: Monaco measures once at creation.
  • Solution: Enable automaticLayout: true, or call editor.layout() after the container resizes.

Language namespaces are missing after importing editor.api.js (0.55.0+)

  • Cause: As of 0.55.0 the language namespaces (e.g. monaco.languages.typescript) are no longer attached when importing the API-only entry editor.api.js; they were moved. (See issue #5346.)
  • Solution: Import editor.main (which registers languages), or explicitly import the language contributions you need (Section 11).
  • Prevention: Use editor.main unless you are deliberately doing a minimal opt-in build.

Error: monaco is not loaded yet

  • Cause: Code referenced the global monaco before the AMD loader finished resolving vs/editor/editor.main.
  • Solution: Put all editor code inside the require(['vs/editor/editor.main'], …) callback (Section 1).

Failed to load source map: simpleWorker.nls.js.map

  • Cause: A worker source map referenced in the shipped files isn't served. (See issue #3336.)
  • Solution: This is a dev-tools console warning, not a functional error — it does not affect the editor. Disable source-map fetching in dev tools or ignore it.

Worker NLS file has a doubled extension (nls.messages.<lang>.js.js)

  • Cause: A packaging bug in some 0.55.x builds produces a .js.js suffix for localized NLS message files. (See issue #5345.)
  • Solution: If you load localized messages, verify the emitted filename and adjust your copy step; track the linked issue for a fixed release.

Right-click "Paste" does nothing

  • Cause: Browsers restrict programmatic clipboard reads; the context-menu paste cannot access the system clipboard without a user gesture/permission. (See issue #5158.)
  • Solution: Use the keyboard shortcut (Ctrl/Cmd+V), which the browser permits.

Content Security Policy blocks the editor or workers

  • Cause: Monaco creates workers (and, for cross-origin workers, Blob URLs) and injects styles. A strict CSP can block these.
  • Solution: Allow worker-src blob: (and script-src blob: if you use the cross-origin Blob proxy) and the worker origins. If you use Trusted Types, provide a MonacoEnvironment.createTrustedTypesPolicy (see the Admin Guide's security section).

14. FAQ

Q: What's the difference between monaco-editor and monaco-editor-core? monaco-editor-core is the editor engine, built from the VS Code source. monaco-editor is what you install — it bundles the core plus all the language definitions and language services. Use monaco-editor.

Q: Do I need a server? No. Monaco is entirely client-side. The "workers" are browser web workers, not server processes.

Q: Why are there so many .worker.js files? Each rich language service (TypeScript, JSON, CSS, HTML) runs in its own web worker so heavy analysis doesn't block the UI. The core editor has one worker too. You wire them up in MonacoEnvironment (Section 3).

Q: Can I use Monaco without any workers? Yes, with reduced functionality. Return the core editor.worker for every label and you get syntax highlighting and editing, but no TypeScript/JSON/CSS/HTML IntelliSense or validation.

Q: How do I get TypeScript autocompletion for my own libraries? Call monaco.languages.typescript.typescriptDefaults.addExtraLib(declarationSource, uri) with your .d.ts content (Section 8.2).

Q: How do I make the editor read-only? Pass readOnly: true at creation or call editor.updateOptions({ readOnly: true }).

Q: How do I change the theme for just one editor? You can't — monaco.editor.setTheme is global. To show different color schemes, render editors on separate pages/iframes, or switch the global theme when focus changes.

Q: Which languages have full IntelliSense vs. just highlighting? Full language services: TypeScript/JavaScript, JSON, CSS/SCSS/LESS, HTML/Handlebars/Razor. All other bundled languages get highlighting (Monarch) only.

Q: How do I support a language that isn't bundled? Register it with monaco.languages.register({ id }) and provide a Monarch tokenizer via monaco.languages.setMonarchTokensProvider, plus any providers you need. See the Playground's "custom languages" example.

Q: Is Monaco mobile/touch friendly? Monaco targets desktop browsers. There is an iPadShowKeyboard helper and basic touch support, but the editor is optimized for keyboard-and-mouse use.

Q: How do I update to a new version? Bump monaco-editor in your package.json and re-bundle. Watch the changelog for breaking changes — the 0.55.0 language-namespace move (issue #5346) is an example of a change that affected API-only imports.

Q: Where is the official live editor to experiment in? The Monaco Playground (see Reference below) lets you run and tweak every example in the browser.


15. Reference

Import-path cheat sheet

What ESM path
Full editor (all languages/features) monaco-editor or monaco-editor/esm/vs/editor/editor.main.js
Editor API only monaco-editor/esm/vs/editor/editor.api
Core editor worker monaco-editor/esm/vs/editor/editor.worker.js
JSON worker monaco-editor/esm/vs/language/json/json.worker.js
CSS worker monaco-editor/esm/vs/language/css/css.worker.js
HTML worker monaco-editor/esm/vs/language/html/html.worker.js
TypeScript/JS worker monaco-editor/esm/vs/language/typescript/ts.worker.js

Worker label map

label Worker
json json worker
css / scss / less css worker
html / handlebars / razor html worker
typescript / javascript ts worker
(default) editor worker

Built-in themes

vs · vs-dark · hc-black · hc-light

Key API entry points

Call Purpose
monaco.editor.create(el, opts) Create an editor.
monaco.editor.createDiffEditor(el, opts) Create a diff editor.
monaco.editor.createModel(value, lang, uri?) Create a text model.
monaco.editor.setTheme(name) Set the global theme.
monaco.editor.defineTheme(name, data) Define a custom theme.
monaco.editor.setModelLanguage(model, lang) Change a model's language.
monaco.languages.register({ id }) Register a language id.
monaco.languages.getLanguages() List registered languages.
monaco.languages.typescript.typescriptDefaults Configure the TS service.
monaco.languages.json.jsonDefaults Configure the JSON service.
monaco.languages.css.cssDefaults Configure the CSS service.
monaco.languages.html.htmlDefaults Configure the HTML service.
editor.addCommand(keybinding, handler) Bind a keyboard command.
editor.addAction(descriptor) Add a palette/context-menu action.
editor.updateOptions(opts) Change editor options.
editor.dispose() Release the editor.

Official resources

  • Playground (runnable examples): https://microsoft.github.io/monaco-editor/playground.html
  • API reference (typedoc): https://microsoft.github.io/monaco-editor/typedoc/index.html
  • Website & docs: https://microsoft.github.io/monaco-editor/
  • Source & issues: https://github.com/microsoft/monaco-editor

Note

The exhaustive list of editor options, model APIs, and provider interfaces is generated from the TypeScript declarations and published in the typedoc API reference. When you need an option or method this guide doesn't cover, that reference is the authoritative source for your installed version.