This guide is for the engineers, architects, and release maintainers who integrate, build, ship, or extend the monaco-editor package. It explains what the repository actually is (a distribution layer over the VS Code editor), how the build pipeline turns the VS Code source tree into the monaco-editor and monaco-editor-core npm packages, the structure of every sub-package, the worker and security model, and the operational concerns of running Monaco in production.
Important
microsoft/monaco-editoris not the source of the editor engine. The engine lives inmicrosoft/vscode. This repository pins a specific VS Code commit (vscodeRef), builds it into the publishablemonaco-editor-corepackage, then wraps that core with language definitions, language services, a webpack plugin, an LSP client, samples, and a website. Understanding this "published-and-consumed core" relationship is the key to everything below.
1. Executive summary
| Attribute | Value |
|---|---|
| Product | Monaco Editor — the browser code editor that powers VS Code |
| This repository | Distribution/packaging layer + language definitions + tooling |
| Primary package | monaco-editor |
| Version (this build) | 0.55.1 |
| Engine package | monaco-editor-core (built from VS Code) |
Pinned VS Code commit (vscodeRef) |
86f5a62f058e3905f74a9fa65d04b2f3b533408e |
| License | MIT |
| Author | Microsoft Corporation |
| Source commit documented here | 4cd0f4f (2026-06-02) |
| Runtime | Browser (client-side only; uses web workers) |
| Build toolchain | TypeScript, ts-node, Rollup, esbuild, webpack, Playwright |
What an operator needs to know in 30 seconds: Monaco is a client-side, zero-backend library. Its only "servers" are browser web workers. You consume it as an npm package or a CDN bundle. The repository's release engineering is a two-stage build: stage 1 compiles the editor out of a pinned VS Code commit into monaco-editor-core; stage 2 wraps that core into monaco-editor, runs cross-bundler smoke tests, and publishes. Versions are traceable back to exact VS Code and monaco-editor commits stamped into package.json at build time.
2. Architecture overview
2.1 Layered model
┌─────────────────────────────────────────────────────────────┐
│ Your application (web page / SPA / Electron renderer) │
│ import * as monaco from 'monaco-editor' │
└───────────────────────────────┬─────────────────────────────┘
│
┌───────────────────────────────▼─────────────────────────────┐
│ monaco-editor (THIS REPO — distribution layer) │
│ src/index.ts ─ re-exports editor.api + registers: │
│ • features/register.all (editor contributions) │
│ • languages/definitions (~90 Monarch tokenizers) │
│ • languages/features (CSS/HTML/JSON/TS services) │
│ • @vscode/monaco-lsp-client (LSP client, optional) │
└───────────────────────────────┬─────────────────────────────┘
│ import 'monaco-editor-core'
┌───────────────────────────────▼─────────────────────────────┐
│ monaco-editor-core (published separately, built from VSCode) │
│ esm/vs/editor/editor.api ─ the `monaco` namespace │
│ the editor widget, view, model, tokenizer, worker host │
└───────────────────────────────┬─────────────────────────────┘
│ built from a pinned commit
┌───────────────────────────────▼─────────────────────────────┐
│ microsoft/vscode @ vscodeRef │
│ gulp editor-distro → out-monaco-editor-core │
└─────────────────────────────────────────────────────────────┘
2.2 The wrapper entry point
The whole packaging layer is a handful of small re-export modules. The public entry is src/index.ts:
import * as lsp from '@vscode/monaco-lsp-client';
import { css, html, json, typescript } from './languages/register.all';
import './features/register.all';
// ensure that the main monaco-editor entry-point loads all features from monaco-editor-core
import 'monaco-editor-core';
export * from './editor';
export { css, html, json, typescript, lsp };
And src/editor.ts is a single line that re-exports the engine's public API:
export * from 'monaco-editor-core/esm/vs/editor/editor.api';
Note
This is why the User Guide says "Monaco" means
monaco-editor-core's API. Themonaco.editor.*andmonaco.languages.*surface comes from the core; this repository's job is to register the language tokenizers and the editor feature contributions into that core, plus ship the language-service workers.
2.3 Feature and language registration
src/features/register.all.ts is a flat list of side-effect imports — one per editor contribution (anchorSelect, bracketMatching, find, folding, hover, suggest, inlineCompletions, gpu, stickyScroll, and ~60 more). Each */register module pulls the corresponding contribution from monaco-editor-core. Importing monaco-editor (which imports register.all) therefore activates every editor feature.
src/languages/register.all.ts registers both:
./definitions/register.all— the ~90 Monarch tokenizer definitions insrc/languages/definitions/<lang>/../features/register.all— the four rich language services (css,html,json,typescript).
This split (definitions = highlighting; features = language services) is the architectural reason the User Guide distinguishes "basic languages" from "rich language services".
2.4 The language-service adapter layer
The four rich services are powered by Microsoft's standalone language-service libraries (vscode-css-languageservice, vscode-html-languageservice, vscode-json-languageservice, and the TypeScript compiler) running in web workers. src/languages/features/common/lspLanguageFeatures.ts is the adapter layer that maps those LSP-style services onto Monaco's provider interfaces. Its classes are the bridge:
| Adapter class | Monaco capability it provides |
|---|---|
DiagnosticsAdapter |
Live validation markers |
CompletionAdapter |
Autocomplete suggestions |
HoverAdapter |
Hover tooltips |
DocumentHighlightAdapter |
Occurrence highlighting |
DefinitionAdapter |
Go-to-definition |
ReferenceAdapter |
Find-all-references |
RenameAdapter |
Symbol rename |
DocumentSymbolAdapter |
Outline / breadcrumbs |
DocumentLinkAdapter |
Clickable links |
DocumentFormattingEditProvider / DocumentRangeFormattingEditProvider |
Formatting |
DocumentColorAdapter |
Color decorators/picker |
FoldingRangeAdapter |
Folding regions |
SelectionRangeAdapter |
Smart-select expand/shrink |
Tip
If you are building your own rich language service, this adapter file is the reference implementation for wiring an LSP-style server (one that speaks the language-server feature set) into Monaco's provider API.
2.5 The worker host
src/internal/common/workers.ts and initialize.ts implement the bridge between Monaco's worker host and the browser:
getWorker(descriptor)consultsglobalThis.MonacoEnvironmentfor eithergetWorker(moduleId, label)orgetWorkerUrl(moduleId, label). If neither is defined and nocreateWorkeris supplied, it throws the familiarYou must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorkererror.createWebWorker<T>(opts)wraps the resolvedWorkerand hands it to the core'seditor.createWebWorker.- A Trusted Types policy (
defaultWorkerFactory) is created viaMonacoEnvironment.createTrustedTypesPolicy(if the host provides one) orglobalThis.trustedTypes.createPolicy, so worker script URLs are minted through a CSP-compliant policy.
3. Repository structure
| Path | Purpose | Published as |
|---|---|---|
src/ |
The wrapper: index.ts, editor.ts, features/, languages/, internal/ |
part of monaco-editor |
src/languages/definitions/<lang>/ |
Monarch tokenizers (~90 languages) + grammar tests | part of monaco-editor |
src/languages/features/{css,html,json,typescript}/ |
Rich language-service contributions + workers | part of monaco-editor |
src/deprecated/ |
Old import paths kept for back-compat | part of monaco-editor |
monaco-lsp-client/ |
@vscode/monaco-lsp-client — LSP client package |
separate npm package |
webpack-plugin/ |
monaco-editor-webpack-plugin (v7.1.1) |
separate npm package |
samples/ |
Integration samples for every bundler + Electron/NW.js | monaco-editor-samples (not published to consumers) |
website/ |
The monaco-editor.github.io site, playground, typedoc | deployed to GitHub Pages |
test/manual/ |
Manual interactive test harness | dev only |
test/smoke/ |
Playwright cross-bundler smoke tests | dev only |
scripts/ci/ |
Release build orchestration (TypeScript) | dev only |
scripts/lib/ |
Shared build helpers (git clone, run, versioning) | dev only |
build/ |
Editor build, postinstall, sample checks, npm helpers | dev only |
Note
samples/legacy/contains AMD-based samples (browser-amd-*,electron-amd*,nwjs-amd*). Per the repo's own note these "are not supported anymore and will be removed in future releases." Steer integrators to the ESM samples.
4. Build & release pipeline
The release is a two-stage process orchestrated by TypeScript files under scripts/ci/, each runnable with a stable or nightly argument.
4.1 Stage 1 — build monaco-editor-core from VS Code
scripts/ci/build-monaco-editor-core-pkg.ts:
- Reads
versionandvscodeReffrom the rootpackage.json. - For
stable: uses those values verbatim. Fornightly: computes a version withgetNightlyVersion()(→0.<minor+1>.0-<prerelease>, where<prerelease>interpolates today's date) and readsVSCODE_REFfrom the environment. - Shallow-clones
microsoft/vscodeat the ref andmicrosoft/vscode-loc(localizations) as a sibling. - Stamps the resolved version and
vscodeCommitIdinto VS Code'sbuild/monaco/package.json. - Runs the full VS Code build & test gate, then
gulp editor-distro, producingout-monaco-editor-coreready to publish.
The VS Code build/test gate (buildAndTest()) runs, in order:
npm install
npm run playwright-install
npm run gulp hygiene
npm run valid-layers-check
npm run eslint
npm run monaco-compile-check
npm run --max_old_space_size=4095 compile
npm run gulp editor-distro
npm run test-browser -- --browser chromium
# typings smoke test: tsc against out-monaco-editor-core
# monaco tests: esm-check, bundle-webpack, compile, test (in vscode/test/monaco)
4.2 Stage 2 — build monaco-editor from the core
scripts/ci/build-monaco-editor-pkg.ts:
npm ciinwebpack-plugin/.- Sets the
monaco-editorversion(stable: the package version; nightly: computed) and pinsdevDependencies['monaco-editor-core']to the matching core version. npm installto pull the new core.- CVE tracking: merges
monaco-editor-core's runtimedependenciesintomonaco-editor'sdependencies(throwing if any key would be overwritten) so the published package's dependency tree reflects what the core actually bundles. - Stamps
vscodeCommitId(from the core) andmonacoCommitId(this repo's HEAD) intopackage.jsonfor full provenance. - Builds and tests:
npm run build # build-lsp + build-monaco-editor
npm test # check-samples + grammar tests
npm run compile # webpack-plugin
npm run package-for-smoketest-webpack
npm run package-for-smoketest-esbuild
npm run package-for-smoketest-vite
npm run smoketest # Playwright across the packaged bundles
# → ./out/monaco-editor ready to publish
4.3 Environment contract
scripts/ci/env.ts requires two environment variables for nightly builds, failing fast if absent:
| Variable | Used for |
|---|---|
VSCODE_REF |
Which VS Code commit to build the nightly core from. |
PRERELEASE_VERSION |
The prerelease suffix template (supports ${today}). |
Important
Provenance is a first-class design goal here. Every published
monaco-editorrecordsvscodeCommitIdandmonacoCommitId. Given any installed version you can determine the exact VS Code commit and monaco-editor commit it was built from — critical for security triage and reproducing bugs.
4.4 Local build scripts (root package.json)
| Script | Action |
|---|---|
build |
build-lsp then build-monaco-editor. |
build-monaco-editor |
ts-node ./build/build-monaco-editor. |
build-lsp |
cd monaco-lsp-client && npm install && npm run build. |
build-all |
build plus packaging all smoke-test fixtures. |
watch |
tsc -w -p ./src. |
import-typescript |
ts-node ./build/importTypescript (refreshes the bundled TS lib). |
simpleserver |
ts-node ./build/simpleserver (serve locally). |
test |
build/check-samples + test:grammars. |
test:grammars |
node --test over src/languages/definitions/*/*.test.ts. |
smoketest |
playwright test --config=test/smoke/playwright.config.ts. |
5. The webpack plugin (monaco-editor-webpack-plugin)
A separately published package (v7.1.1) that frees integrators from hand-writing the MonacoEnvironment worker wiring.
5.1 What it does
On apply(compiler) it:
- Reads
metadata.jsfrom the resolvedmonaco-editorinstall to learn every available feature and language and their worker entry points. - Resolves the desired subset from the user's
languages/featuresoptions. - Generates a
MonacoEnvironmentglobal (via an include-loader rule oneditor.api|main.js) whosegetWorkerUrlmaps each label to the emitted worker filename — including the cross-origin Blob-proxy fallback. - Registers an
AddWorkerEntryPointPluginper worker, each wrapped with webpack'sLimitChunkCountPlugin({ maxChunks: 1 })so every worker compiles to a single file.
5.2 Options
| Option | Type | Default | Purpose |
|---|---|---|---|
languages |
EditorLanguage[] |
all | Include only this subset of languages. |
customLanguages |
IFeatureDefinition[] |
— | Add languages not shipped with monaco-editor. |
features |
(EditorFeature | NegatedEditorFeature)[] |
all | Include a subset; prefix with ! to exclude (e.g. '!contextmenu'). |
filename |
string |
'[name].worker.js' |
Worker filename template (supports [contenthash]). |
monacoEditorPath |
string |
auto-resolved | Absolute path to the monaco-editor module. |
publicPath |
string |
'' |
Override the public path workers load from (useful for CDNs / cross-origin). |
globalAPI |
boolean |
false |
Expose the editor API as a global monaco object (the ESM build does not by default since 0.22.0). |
Note
Feature exclusion is all-or-subset: if any entry in
featuresstarts with!, the plugin treats the list as "all features minus the negated ones"; otherwise the list is treated as an explicit allow-list. Worker sharing is built in — thetypescriptworker is reused forjavascript, thecssworker forscss/less, and thehtmlworker forhandlebars/razor.
5.3 Compatibility
| Peer dependency | Range |
|---|---|
webpack |
^4.5.0 || 5.x |
monaco-editor |
>= 0.31.0 |
Runtime dependency: loader-utils (for worker filename interpolation).
6. The LSP client (@vscode/monaco-lsp-client)
A separate package (v0.1.0) for connecting Monaco to a real Language Server over a transport. Its public surface:
export { MonacoLspClient, WebSocketTransport, createTransportToWorker, createTransportToIFrame };
| Export | Purpose |
|---|---|
MonacoLspClient |
Connects an LSP server to Monaco's language-feature registries. |
WebSocketTransport |
LSP over a WebSocket (server-backed language servers). |
createTransportToWorker |
LSP to a server running in a web worker. |
createTransportToIFrame |
LSP to a server running in an iframe. |
It is built with rolldown and depends on the @hediet/json-rpc family for the typed RPC contract. A code generator (monaco-lsp-client/generator/index.ts, LSPTypesGenerator) reads the LSP metaModel.json and emits src/types.ts — typed request/notification contracts plus a capabilities map and an api contract object. Regenerate with npm run generate (tsx generator/index.ts).
Warning
Known issue #5340:
MonacoLspClientdoes not dispose its language-feature registrations, which can leak provider registrations across reconnects. Known issue #5342:textDocument/codeActionrequests omit diagnostic fields (data,code,source) some servers need for quick-fixes. Track both if you build on this client; it is an early-version (0.1.0) package.
7. Worker & runtime model
- Everything is client-side. There is no server component. "Workers" are browser web workers.
- One core worker + four service workers. The core
editor.workerhandles text diffing, tokenization handoff, and link detection. Each rich service (ts.worker,json.worker,css.worker,html.worker) runs its language analysis off the UI thread. - Worker resolution is host-supplied. Monaco never hard-codes worker URLs; the host provides them through
MonacoEnvironment(Section 2.5). Bundler plugins/configs generate that wiring. - Idle models.
createWebWorkeracceptskeepIdleModels; by default idle models stop syncing to the worker after a while to save memory.
8. Security
Caution
Monaco renders untrusted text and, for some features (hovers, suggestion docs), renders Markdown. Treat the following as required hardening when embedding Monaco in a security-sensitive context.
8.1 Trusted Types & CSP
The worker host mints worker script URLs through a Trusted Types policy named defaultWorkerFactory. If your page enforces Trusted Types, supply MonacoEnvironment.createTrustedTypesPolicy(name, options) so Monaco routes script-URL creation through your approved policy. For Content Security Policy, allow worker-src blob: (Monaco may construct workers from Blob URLs, especially in the cross-origin case) and the worker script origins.
8.2 DOMPurify advisory (CVE-2026-0540)
Monaco renders Markdown (in hovers and suggestion documentation) using marked + dompurify, carried transitively via the bundled core. Issue #5248 tracks CVE-2026-0540, a cross-site scripting vulnerability in DOMPurify.
- Mitigation: Upgrade to a
monaco-editorrelease whose bundled core ships a patched DOMPurify. Because the two-stage build merges core dependencies into the publishedmonaco-editorpackage.json(Section 4.2), you can audit the DOMPurify version directly from your installed package's dependency tree. - Defense in depth: Do not feed fully attacker-controlled HTML into hover/markdown content you register via custom providers.
8.3 Pinned security-sensitive deps
Issue #5314 asks why marked and dompurify versions are fixed — the answer is deliberate: pinning these markdown/sanitization dependencies keeps the security surface auditable and reproducible per release. Do not float them independently of the published lockstep.
8.4 Electron embedding
The ESM Electron sample uses webPreferences: { worldSafeExecuteJavaScript: true }. A legacy nodeIntegration-enabled sample exists (samples/legacy/electron-amd-nodeIntegration) with nodeIntegration: true, sandbox: false, contextIsolation: false — that configuration disables Electron's renderer sandbox and should not be used as a template for production apps. Prefer context isolation and a sandboxed renderer.
9. Operations
9.1 Deploying Monaco assets
Monaco is static assets. Production concerns are:
- Serve the worker files at the URLs your
MonacoEnvironmentreturns. A 404 on a worker disables that language's features. - Same-origin or cross-origin proxy. If workers live on a CDN, either set the webpack plugin's
publicPathor use the Blob-proxy pattern (User Guide §3.6). - Caching. Worker and editor bundles are content-addressable when you use
[contenthash]filenames; cache them aggressively and bust on version bumps. - Fonts/icons. Monaco ships a
codicon.ttf; ensure your bundler emits it (e.g. webpackasset/resourcefor.ttf).
9.2 Health & smoke checks
The repository's own test/smoke/ Playwright suite packages the editor with webpack, esbuild, and Vite and verifies it boots and finds its workers. The playwright.config.ts honors CI and PORT environment variables. Adopt the same approach for your integration: a headless smoke test that mounts an editor and asserts a TypeScript completion appears proves your worker wiring end to end.
9.3 Performance tuning
- Disable the minimap (
minimap: { enabled: false }) andrenderWhitespacefor large documents. - Use the webpack plugin's
languages/featuresallow-list or theeditor.apiopt-in build to cut bundle size. - Reuse models across editors instead of recreating them; dispose aggressively to free workers.
10. Troubleshooting (operational)
Worker 404 / features silently disabled
- Cause: The worker URL your
MonacoEnvironmentreturns isn't served. - Solution: Confirm the worker files are emitted and reachable at those paths; check the network tab for 404s on
*.worker.js.
Error: monaco is not loaded yet
- Cause: Global
monacoaccessed before the AMD loader finished (website/src/monaco-loader.tsraises this). - Solution: Access
monacoonly inside the loader callback / after the editor import resolves.
Error: InstantiationService has been disposed
- Cause: An editor or service was used after disposal (issue #4612).
- Solution: Ensure you don't call into an editor/model after
dispose(); in SPAs, guard async callbacks that may fire after teardown.
Build error: Invalid argument
- Cause: A CI build script was invoked without
stableornightly(build-monaco-editor-core-pkg.ts/build-monaco-editor-pkg.tsthrow this). - Solution: Pass the channel argument explicitly.
Build error: Missing PRERELEASE_VERSION / Missing VSCODE_REF in process.env
- Cause: A nightly build ran without its environment contract (
scripts/ci/env.ts). - Solution: Set both env vars before a nightly build.
Auto-completion disabled for languages with id ≥ 256
- Cause: A LanguageID/TokenType bit-overlap bug (issue #5344, closed).
- Solution: Update to a release that includes the fix if you register very large numbers of custom languages.
11. Testing strategy
| Layer | Tooling | Location |
|---|---|---|
| Grammar/tokenizer tests | node --test (built-in test runner) |
src/languages/definitions/*/*.test.ts |
| Sample integrity | build/check-samples |
run via npm test |
| Cross-bundler smoke | Playwright | test/smoke/ (webpack/esbuild/vite/parcel/amd) |
| Manual interactive | hand-run harness | test/manual/ |
| Core editor tests | VS Code's own suite (stage 1) | vscode/test/monaco |
The smoke tests are the integration safety net: they package the actual published shape of the editor with each bundler and run it in a real browser. The grammar tests validate every Monarch tokenizer against fixtures so language highlighting changes are caught.
12. Module reference
| Module | Role |
|---|---|
src/index.ts |
Public entry; registers features + languages + LSP, re-exports the core API. |
src/editor.ts |
One-line re-export of monaco-editor-core editor API. |
src/features/register.all.ts |
Side-effect imports activating every editor contribution. |
src/languages/definitions/ |
Monarch tokenizers for ~90 languages (+ grammar tests). |
src/languages/features/common/lspLanguageFeatures.ts |
Adapter layer mapping LSP-style services to Monaco providers. |
src/languages/features/{css,html,json,typescript}/ |
Per-language service contributions + workers. |
src/internal/common/workers.ts |
Worker resolution + Trusted Types policy + createWebWorker. |
src/internal/common/initialize.ts |
Worker bootstrap (self.onmessage → core worker start). |
webpack-plugin/src/index.ts |
MonacoEditorWebpackPlugin — worker wiring + bundle slimming. |
monaco-lsp-client/src/ |
LSP client (MonacoLspClient, transports). |
monaco-lsp-client/generator/ |
LSP type/contract generator from metaModel.json. |
scripts/ci/* |
Two-stage release build orchestration. |
scripts/lib/index.ts |
Build helpers: run, gitShallowClone, getNightlyVersion, version stamping. |
13. Potential improvements & known debt
- LSP client maturity.
@vscode/monaco-lsp-clientis v0.1.0 with open disposal (#5340) and code-action field (#5342) bugs. Treat as experimental; pin and test before depending on it. - Two ESM variants requested. Issue #5067 ("Ship two esm variants") and #886 ("ESM and CSS loading") point at ongoing friction in the ESM/CSS delivery story; expect packaging changes across minor versions.
- NLS double-extension packaging bug. Issue #5345 — localized
nls.messages.<lang>.js.jsfiles in0.55.x. If you ship localizations, verify emitted filenames. - Accessibility backlog. Several open a11y issues (focus management after closing search #5328, tab order in find/replace #5329, suggestion-list contrast #5307, system-theme adoption #5312). Audit against WCAG if accessibility is a requirement, and consider the high-contrast themes.
- Legacy AMD samples.
samples/legacy/is explicitly slated for removal; do not build new integrations on the AMD path.
14. Quick reference
Provenance fields in package.json
| Field | Meaning |
|---|---|
version |
Published monaco-editor version (0.55.1). |
vscodeRef |
VS Code commit the core is built from. |
vscodeCommitId |
Resolved VS Code commit (stamped at build). |
monacoCommitId |
This repo's commit (stamped at build). |
Entry points
| Entry | Use |
|---|---|
./esm/vs/editor/editor.main.js |
Full editor (ESM). |
./min/vs/editor/editor.main.js |
Full editor (AMD/min, main). |
./esm/vs/editor/editor.api |
Editor API only. |
CI build invocations
# Stage 1: build monaco-editor-core from VS Code
ts-node scripts/ci/build-monaco-editor-core-pkg.ts stable # or nightly
# Stage 2: build monaco-editor from the core
ts-node scripts/ci/build-monaco-editor-pkg.ts stable # or nightly
Key facts
- License: MIT. Author: Microsoft Corporation.
- No backend; client-side only; web-worker based.
monaco-editorversion0.55.1; VS Code ref86f5a62….- Separate published packages:
monaco-editor,monaco-editor-core,monaco-editor-webpack-plugin(7.1.1),@vscode/monaco-lsp-client(0.1.0).
Official resources
- Repository & issues: https://github.com/microsoft/monaco-editor
- Website / playground / typedoc: https://microsoft.github.io/monaco-editor/
- Engine source (VS Code): https://github.com/microsoft/vscode