Integrating the Monaco code editor into a Svelte project without bundlers (using a CDN) is straightforward. We will load Monaco via a CDN and wrap it in a reusable Svelte component. Follow these steps:
-
Include Monaco via CDN: In your HTML (e.g.
public/index.html
), add the Monaco AMD loader script. For example:<script src="https://cdn.jsdelivr.net/npm/[email protected]/min/vs/loader.js"></script>
This loads Monaco’s AMD loader from a CDN (jsDelivr). Alternatively, install the Monaco loader utility:
npm install @monaco-editor/loader
and use it in your Svelte component (which will internally fetch Monaco from the CDN by default). -
Create a Svelte component: Make a new file, e.g.
MonacoEditor.svelte
, to encapsulate the editor. Inside this component:- Add a
<div>
element that will host the editor, and use Svelte’sbind:this
to get a reference to that DOM node. - In the
<script>
section, import Svelte’s lifecycle functions (onMount
,onDestroy
). If using the Monaco loader utility, import it as well. - Use
onMount
to initialize Monaco on the client side (Monaco requires a browser environment). InonMount
, either call the AMD loader or use the loader utility to get themonaco
instance, then create the editor. - Use
onDestroy
to dispose of the editor and any models to prevent memory leaks when the component is removed.
- Add a
-
Initialize the editor: Within
onMount
, configure the editor’s initial content and language. You can usemonaco.editor.create()
to instantiate the editor in the container div. For example, set a defaultvalue
(code string) andlanguage
(like"javascript"
for JS syntax). This gives you a standalone code editor instance.
Below is a simple MonacoEditor.svelte component using the CDN loader (no bundler needed):
<script>
import { onMount, onDestroy } from 'svelte';
// If using the Monaco loader utility (installed via NPM):
import loader from '@monaco-editor/loader';
let editorContainer;
let editor; // will hold the IStandaloneCodeEditor instance
let monaco; // will hold the monaco API object
onMount(async () => {
// Initialize Monaco Editor (loads from CDN by default)
monaco = await loader.init(); // returns the global monaco instance
// Create the editor in the container
editor = monaco.editor.create(editorContainer, {
value: "// Type your code here\nconsole.log('Hello, Monaco!');",
language: 'javascript',
theme: 'vs-dark' // e.g. use a dark theme; default is 'vs' (light)
});
});
onDestroy(() => {
// Cleanup: dispose editor and any models to avoid memory leaks
editor?.dispose();
monaco?.editor.getModels().forEach((model) => model.dispose());
});
</script>
<!-- The container that Monaco will take over: style it or set width/height as needed -->
<div bind:this={editorContainer} style="width:100%; height:400px;"></div>
In this component:
- We call
loader.init()
to load Monaco (the loader will fetch the Monaco scripts from the CDN). Once the promise resolves, we have themonaco
object available to create editors and models. - We then call
monaco.editor.create(...)
with the container DOM node and an options object. We provided an initial code string invalue
, set the language to JavaScript for syntax highlighting, and chose a theme. - We dispose of the editor and models in
onDestroy
to free resources when the component is unloaded.
Using the component: You can now include <MonacoEditor />
in any parent Svelte component or page. For example, in App.svelte
:
<script>
import MonacoEditor from './MonacoEditor.svelte';
</script>
<MonacoEditor />
This will render the Monaco code editor. No bundler-specific configuration (like Vite or Webpack) is needed; Monaco is loaded dynamically from the CDN.
Note: The Monaco editor is large and uses web workers for IntelliSense and diagnostics. When loaded via CDN, the necessary worker scripts will be fetched on demand from the CDN (make sure your site allows those network requests). Also, since we initialized Monaco in
onMount
, this code runs only in the browser (avoiding SSR issues). If you prefer not to use the loader utility, you could directly use the AMD loader: for example, inonMount
callrequire.config({ paths: { 'vs': 'https://cdn.jsdelivr.net/npm/[email protected]/min/vs' } }); require(['vs/editor/editor.main'], monaco => { ...create editor... });
. Using the official loader utility as shown is often simpler.
Monaco Editor comes with a rich feature set out-of-the-box. When integrated with Svelte via the CDN, you can leverage these features:
Monaco provides syntax highlighting for dozens of languages. The highlighting is activated by setting the model’s language. For example, passing language: "javascript"
in the editor options will apply JavaScript syntax coloring. Monaco has built-in support for common languages (JS/TS, CSS, HTML, JSON, XML, etc.) and you can register custom languages if needed. The editor will automatically color keywords, strings, comments, etc., according to the language’s rules. By default, Monaco uses a light theme ("vs"
). You can switch to the dark theme ("vs-dark"
) or high contrast theme ("hc-black"
) easily, or even define a custom theme (see Themes & customization below).
IntelliSense (code completion suggestions) is a hallmark of Monaco. For supported languages like JavaScript/TypeScript, Monaco will show auto-completion suggestions as you type (or when you press Ctrl+Space) – e.g. suggesting object properties, globals, etc. This is powered by language services (TypeScript language service for JS/TS) running in web workers. Autocomplete suggestions appear in a suggest widget below your cursor, and you can navigate through suggestions with arrow keys. By default, basic word-based suggestions are also available for plain text. Monaco’s API allows you to enhance or customize suggestions (for example, by registering a custom completion item provider via monaco.languages.registerCompletionItemProvider()
for your own language or domain-specific words).
Monaco supports inline completion suggestions – often seen as ghost text that auto-fills ahead of the cursor (similar to GitHub Copilot suggestions). This feature is disabled by default but can be enabled and utilized. To use inline suggestions:
- Enable the editor option for inline suggestions: for example,
monaco.editor.create(..., { inlineSuggest: { enabled: true } })
. This allows ghost text to be shown. - Register an inline completions provider for the desired language using
monaco.languages.registerInlineCompletionsProvider(lang, provider)
. The provider’sprovideInlineCompletions
function should return a list of completion items withinsertText
(the text to insert) and optionally afilterText
orrange
. These will appear as transparent text at the cursor. - When the user accepts an inline suggestion (for example by pressing Tab or End), the text is inserted.
By using inline completions, you can suggest entire lines or phrases as the user types, without the need for them to explicitly trigger the suggestions list. (See the Cookbook for an example of adding a simple ghost-text suggestion.)
Code actions are contextual quick-fixes or refactorings that you can offer to the user. In Monaco, when a code action is available at a given cursor position, a lightbulb icon appears in the editor margin. Clicking it opens a menu of available actions (just like VS Code’s lightbulb). To provide code actions:
- Register a code action provider with
monaco.languages.registerCodeActionProvider(language, provider)
. The provider’sprovideCodeActions
should check the model’s content at the given range and return an array of code actions (each with a title, edit, diagnostics, etc.). - Each code action can specify edits to apply (like insert text, replace text, etc.) and optionally a command to run.
- When Monaco detects that
provideCodeActions
returns an action, it will show the lightbulb at the appropriate line. The user can click it or use the keybinding (usually Ctrl+. in VS Code) to trigger the action.
For example, you could create a code action for JavaScript that suggests “Convert var to let” when it finds the keyword var
, and apply an edit to replace it. When the user triggers the action, Monaco will apply your specified text edit to the model. Code actions allow you to build IDE-like quick fixes into the editor.
CodeLens are those little inline informational widgets that appear above source lines (typically used to display references count, test statuses, etc., in VS Code). Monaco supports CodeLens as well:
- You can register a CodeLens provider via
monaco.languages.registerCodeLensProvider(language, provider)
. The provider’sprovideCodeLenses
should return an array of “lenses”, each with a range (position in the code) and a command (with an id and title). - The CodeLens will render just above the specified line as a small clickable text. If a lens has a command with an
id
, you need to ensure that command is registered so that clicking the lens executes something. Usually you do this by usingeditor.addCommand
to associate a function with that command id. - For example, you might scan the model for every function definition and provide a CodeLens that says “🔎 View References” or “Run Test”. When clicked, your registered command could show a message or navigate somewhere.
Monaco will draw the CodeLens text in a smaller font above the code. They are updated as the code changes (your provider can specify provideCodeLenses
and also an optional resolveCodeLens
if the command or info needs lazy updating). CodeLens is great for adding inline info or actions without disrupting code flow.
Monaco Editor is highly customizable in appearance. There are a few built-in themes:
"vs"
(the default light theme),"vs-dark"
(dark theme),"hc-black"
(high-contrast dark theme).
You can switch theme at runtime using monaco.editor.setTheme('vs-dark')
for example, to immediately apply the dark theme.
For more customization, Monaco lets you define your own themes with monaco.editor.defineTheme(themeName, themeData)
. The themeData
includes colors for syntax tokens, UI widget colors, gutter, background, etc. Once defined, you can set it with monaco.editor.setTheme(themeName)
.
Beyond colors, you can also customize the editor’s other settings via the options in monaco.editor.create
or editor.updateOptions()
. For example, you can enable line numbers, minimap, font size, tab size, whether the editor is read-only, and many more. All of these can be tweaked without any bundler – they are just function calls on the monaco
object.
Diagnostics are how Monaco represents errors, warnings, or informational messages in the code (similar to red squiggly underlines in VS Code). In Monaco, diagnostics are known as markers. You create markers for a model to indicate issues at specific ranges:
- Use
monaco.editor.setModelMarkers(model, owner, markersArray)
to set an array of markers on a given model. Each marker is an object with properties likeseverity
,message
,startLineNumber
,startColumn
,endLineNumber
,endColumn
, etc. - The
severity
should use themonaco.MarkerSeverity
constants: e.g.monaco.MarkerSeverity.Error
,Warning
,Info
, orHint
. This will determine the color of the squiggly (red for error, green/blue for info, etc.). - Once markers are set, Monaco will underline the text range with a colored squiggly and show an icon in the gutter. Hovering over the squiggly or the gutter icon will show the error/warning message.
- Markers persist until you clear or update them. Calling
setModelMarkers
again with a new list (or an empty list) for the same owner will replace the old markers.
For example, you could validate the content of the editor (perhaps by calling an API or running a checker) and then use markers to highlight syntax errors or linter warnings. This provides immediate feedback to the user as they code. Diagnostics in Monaco behave similarly to VS Code's problems view, but you can control them entirely in the browser environment.
To effectively use Monaco Editor in Svelte, it's important to understand some core concepts of Monaco's architecture:
The editor is the primary UI component. In Monaco’s API, a standalone editor is created by monaco.editor.create(containerElement, options)
. This returns an instance of IStandaloneCodeEditor
. The editor instance provides methods to get or set the content, move the cursor, add listeners for changes, etc. Under the hood, the editor displays one text model at a time (see Models below). You can have multiple editor instances on a page, each in its own container. There’s also a standalone diff editor (monaco.editor.createDiffEditor
) for side-by-side comparisons. The editor is highly configurable via the options passed on creation (and via editor.updateOptions()
later). For example, you can control the theme, whether minimap is shown, cursor style, auto-completion toggles, and more. The editor instance is central – you’ll use it to invoke commands, add decorations, or interact with the content programmatically.
A model in Monaco represents the text buffer (the content of a file, essentially). It is an object implementing ITextModel
. You can create a model independently with monaco.editor.createModel(text, language, uri)
. If you don’t create a model explicitly, calling monaco.editor.create
with a value
will create an implicit model for that editor. Models are important because Monaco can have multiple models loaded (like open files in memory), and you can switch the editor to show a different model with editor.setModel()
. Models also hold the language ID for the content, and you can set or change the language of a model via monaco.editor.setModelLanguage(model, languageId)
. Each model can be identified by a monaco.Uri
— if you provide a uri
when creating it (e.g., monaco.Uri.file("app.js")
), Monaco can associate language based on the file extension and manage unique models. Models fire events (e.g. onDidChangeContent
) which you can listen to for detecting changes. When you’re done with a model (e.g., the editor closed a file), remember to dispose it (model.dispose()
), otherwise it stays in memory. In summary: the model is the in-memory text document, and the editor is the viewer/controller for it.
Monaco supports multiple programming languages. Each model is tagged with a language ID (like "javascript"
, "python"
, "cpp"
, etc.). Monaco comes with a set of built-in language definitions and basic syntax highlight rules. It also has language services for some languages (e.g. code completion, hover info, diagnostics for TS/JS, JSON, etc., powered by web workers running those services). The monaco.languages namespace is where you can interact with languages:
- You can get a list of available languages with
monaco.languages.getLanguages()
. - To add support for a new language, use
monaco.languages.register({ id: 'myLang', ... })
to register a language ID, and then register tokens provider for syntax (monaco.languages.setMonarchTokensProvider
) and other providers (completion, hover, etc.) as needed. - For existing languages, you can augment their behavior. For example,
monaco.languages.registerCompletionItemProvider('javascript', {...})
can add custom completions to JavaScript, ormonaco.languages.registerHoverProvider('python', {...})
can provide hover tooltips for Python code. - Monaco uses the language ID on a model to decide which language rules and providers to use for that model’s content. Typically, when you create a model or editor with a language option, Monaco sets up everything (highlighting, etc.) automatically.
In practice, if you’re just using built-in languages, you set the language in the editor options or model, and Monaco handles the rest. If you need a custom language or to extend a language’s capabilities, the monaco.languages
API is your friend.
Both decorations and diagnostics highlight regions of code, but they serve different purposes:
- Decorations are general-purpose markers you can add to highlight or style ranges of text. Use
editor.deltaDecorations(oldDecorations, newDecorations)
to add or remove them. Decorations can change text background, outline, underline, font style, add glyphs in the margin, etc., based on an CSS-like class or style you provide. They are often used for things like highlighting all occurrences of a search term, or marking modified lines, or underlining a variable usage (not necessarily an error). Decorations do not inherently carry a severity or message, they are purely visual (though you could combine them with an overlay widget or tooltip if needed). - Diagnostics (Markers) are specifically for errors/warnings/info. They not only underline text (with squiggly lines) but also carry a message, severity, and appear in the gutter with an icon. Diagnostics are set via markers (using
setModelMarkers
as described earlier). They are tied to the content of the model and typically come from some validation logic (like a linter, compiler, or language service). Markers also feed the editor’s "Problems" list if you implement one. In Monaco, under the hood, diagnostics are implemented using decorations too (with specific squiggly styles), but you should use the marker API for errors so that the user gets consistent behavior (e.g., hover to see the error message, etc.).
So, use decorations when you want to visually emphasize text (without it being an error), and use diagnostics/markers when you want to signal an error, warning, or info to the user. Both can be used simultaneously; e.g., you might decoration-highlight all occurrences of a word, while also having a marker on one occurrence that is an error.
Monaco has an internal command system for handling keybindings and commands (similar to VS Code’s command palette). As a developer, you can add your own commands to an editor instance. A command in Monaco is basically a function that can be triggered via a key combo or programmatically.
- To add a command programmatically, use the editor instance’s
addCommand(keybinding, handler, context?)
method. Thekeybinding
is defined usingmonaco.KeyMod
andmonaco.KeyCode
(for example,monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S
for Ctrl/Cmd+S). The handler is a function to execute when that key is pressed while the editor is focused. This returns a command ID. - Alternatively, use
editor.addAction(descriptor)
. An action is like a command with extra metadata (id, label, keybinding, maybe context) and it shows up in the editor's context menu or palette. For example, you can define an action with a name and it can be triggered via a key or the right-click menu. - Monaco includes many default keybindings (e.g. F1 opens the command palette, Ctrl+F finds text, etc.). Your custom commands can override or complement these.
Keybindings in Monaco use the KeyCode
and KeyMod
enumerations:
monaco.KeyMod
provides modifier keys:.CtrlCmd
(Control on Windows/Linux, Command on Mac),.Shift
,.Alt
,.WinCtrl
(Windows key on Windows, Ctrl on Mac).monaco.KeyCode
provides codes for individual keys: e.g..KeyS
for the "S" key,.Enter
,.F2
, etc. You combine them with a bitwise OR. For example,monaco.KeyMod.Shift | monaco.KeyCode.F2
would represent Shift+F2.
When you add a command with a keybinding, Monaco will call your handler when that key combo is pressed (if no other higher-priority command overrides it). You can also execute commands by their ID using editor.trigger(source, commandId, payload)
if needed, or even programmatically dispatch certain editing commands (Monaco has built-in command IDs for things like cut, paste, etc.).
Monaco Editor contains several built-in UI widgets that appear in response to certain actions:
- Suggest Widget: This is the dropdown list that shows completion suggestions (IntelliSense). It appears by default when you trigger autocompletion. You can programmatically trigger it via
editor.trigger('keyboard', 'editor.action.triggerSuggest', {})
. - Hover Tooltip: When you hover the mouse over a symbol or marker, Monaco can show a hover widget with information (like type info, documentation, or error message). For built-in languages like TS/JS or when you set markers, these hover tooltips work automatically. You can also provide custom hover content by registering a hover provider with
monaco.languages.registerHoverProvider
. - Parameter Hint Widget: If a language supports signature help (parameter hints), Monaco will show a small popup while typing a function call, showing the parameters. This can be triggered via providers (
registerSignatureHelpProvider
). - Glyph Margin (Gutter) Widgets: The gutter (to the left of the code) can show icons for breakpoints, errors, folding, etc. Monaco allows you to add decorations in the gutter (for errors and breakpoints typically). You can capture clicks on the gutter via events if needed (e.g., to implement a simple debugger UI).
- Inline Suggestions Toolbar: If inline completions are enabled, a faint toolbar may show up (as configured, e.g. on hover) allowing users to cycle through suggestions.
- Rename Input and Peek/Reference View: Monaco also has a widget for performing rename across a model (triggered by a rename provider and the F2 key), and a peek view for showing references or definitions inline (like a mini editor). These require implementing the corresponding providers (rename provider, reference provider, etc.) to supply the data.
These widgets are part of Monaco’s UI layer. They will appear as needed if the feature is supported for the current language content. Many of them (suggestions, hover, etc.) you get by default with built-in languages. For custom languages or features, you expose them by implementing the appropriate language provider.
By "inline edits", we refer to features where the editor allows editing something in place with a special UI. The prime example is Rename Symbol: if you implement monaco.languages.registerRenameProvider
, the user can place the cursor on a symbol, press F2, and Monaco will highlight all instances and allow typing a new name, applying the change to all occurrences when done. This is an inline editing experience (a little text box around the symbol). Code actions (the lightbulb fixes) often perform edits too, but those usually apply immediately when chosen (not in-place typing, but an immediate change).
Code Actions we discussed above – they show a lightbulb and usually when executed they apply some edit(s) to the code or perform a refactor. Under the hood, a code action’s edits are just one or more text edits applied to the model (via applyEdits
or through the command framework). When you return a code action in the provider, you can include an edit directly or a command that will apply an edit.
Monaco’s command system and editor model allow you to apply edits programmatically at any time. For example, you can call editor.executeEdits(source, edits)
to make text changes (which will also record them for undo/redo). Each edit is a range plus new text. This is useful if you want to perform an inline transformation from code (like a format or a snippet insertion). The difference from "inline edit UI" is that executeEdits just does it, whereas something like the Rename provider provides a dedicated UI for the user to type.
In summary, inline edits (like rename) and code actions are ways to programmatically modify the code based on user intentions, either through a special UI (rename box) or a lightbulb menu. They make the editor interactive and can significantly enhance the user experience by automating refactors and fixes.
This section provides a quick reference to some of the key Monaco Editor APIs you'll use when integrating with Svelte, organized by their namespace or purpose:
The monaco.editor
namespace contains functions to create and manage editors and models, as well as editor-specific utilities:
monaco.editor.create(container, options)
– Create a new standalone code editor in the given HTML container element with the provided options. Returns anIStandaloneCodeEditor
instance (the editor object you interact with).monaco.editor.createDiffEditor(container, options)
– Create a diff editor for side-by-side comparison of two models.monaco.editor.createModel(text, language?, uri?)
– Create a new text model with initial contenttext
. Optionally specify a language ID and amonaco.Uri
. If no language is given but auri
with a known file extension is provided, Monaco will infer the language. This returns anITextModel
.monaco.editor.getModels()
– Get all currently created models (useful if you want to loop through open files or clean them up).monaco.editor.getModel(uri)
– Retrieve a model by its URI (returnsITextModel
ornull
if not found).monaco.editor.setModelLanguage(model, languageId)
– Change the language associated with a model (will re-highlight accordingly).monaco.editor.setModelMarkers(model, owner, markers)
– Set diagnostics markers on a model.markers
is an array ofIMarkerData
objects (each with at least start/end position, message, severity). This will overwrite any existing markers for that sameowner
name.monaco.editor.defineTheme(themeName, themeData)
– Define a custom theme. ThethemeData
includes color rules for tokens and UI. After defining, usesetTheme
to apply it.monaco.editor.setTheme(themeName)
– Switch the current theme for all Monaco editors.monaco.editor.onDidCreateEditor(callback)
– An event that fires when a new editor instance is created. You might not use this frequently, but it exists for global tracking of editors.
This is the interface for the editor instance returned by monaco.editor.create
. It provides many methods and properties. Key ones include:
- Content Operations:
getValue()
/setValue(string)
– Get or set the full text of the current model in the editor. Setting value will wipe out the undo/redo stack by default (since it’s treated as a whole new content).getModel()
/setModel(ITextModel|null)
– Get the current text model or set a different model to display in this editor. Setting tonull
can detach any model.executeEdits(source, edits)
– Apply a set of text edits (array of edit operations with range and text) to the current model. Use this to programmatically insert or replace text. You can provide asource
string just to identify the edit origin (it can be anything orundefined
).undo()
/redo()
– Perform an undo or redo in the editor.
- Cursor and Selection:
getPosition()
– Get the current cursor position as{ lineNumber, column }
.setPosition({ lineNumber, column })
– Move the cursor to a specified position.getSelection()
– Get the current text selection as aSelection
(range with start/end).setSelection(range)
– Set the selection to a given range (also moves cursor).revealLine(lineNumber)
/revealPosition(position)
– Scroll the editor to bring a given line or position into view.
- Event Listeners:
onDidChangeModelContent(callback)
– Fires whenever the text of the model changes (user typing, edits, etc.). The callback receives an event with details like range of change, etc. Use this to detect user input and maybe sync with a Svelte store or perform validations.onDidChangeCursorPosition
,onDidChangeCursorSelection
– Fires on cursor move or selection change.onKeyDown
/onKeyUp
– Low-level events for key presses in the editor.
- Editor Configuration:
updateOptions(options)
– Change editor options on the fly (like making it read-only, changing line numbers visibility, etc.).layout()
– Recalculate the editor layout. Useful if the container size changed (though Monaco can often auto-adjust ifautomaticLayout
option was set).focus()
/blur()
– Programmatically focus or blur the editor.
- Decorations and Markers:
deltaDecorations(oldDecorationsIds, newDecorations)
– Add or remove decorations. It returns an array of new decoration IDs. Keep those IDs to remove or update the same decorations later. Each decoration innewDecorations
has arange
and anoptions
(which specifies the CSS class or inline style for that decoration, plus other parameters like glyph in gutter or hover message).- (Note: Markers are not set via the editor instance but via
monaco.editor.setModelMarkers
as above, since markers belong to models, not a specific editor.)
- Commands and Actions:
addCommand(keybinding, handler, context)
– Bind a keyboard shortcut to a handler function. Returns a command ID string. Thecontext
is an optional context key if you want the command active only under certain conditions (advanced usage).createContextKey(key, defaultValue)
– Create a context key for the editor (used to control when certain commands/actions are enabled). This is advanced; you might not need it unless you dive into complex keybinding scenarios.addAction(descriptor)
– Add an action with an ID, label, keybindings, and a run function. This also makes the action appear in the editor’s context menu (right-click menu) under "Change All Occurrences" if you setcontextMenuGroupId
and such in the descriptor.trigger(source, actionId, payload)
– Programmatically trigger an editor action or command. For example,editor.trigger('keyboard', 'editor.action.commentLine')
would attempt to toggle comment on the current line (if that action is available).
There are more methods, but these are commonly used ones. The IStandaloneCodeEditor
essentially gives you full control over the editing experience for that editor instance.
This namespace allows you to interact with languages and register language features:
monaco.languages.getLanguages()
– Returns an array of language info objects that Monaco knows about (each has anid
and aliases, extensions, etc.).monaco.languages.register(languageDefinition)
– Register a new language by providing an object with at least anid
and optionally extensions, aliases, etc. After registering, you should also define how the language is highlighted and any language features via the below functions.monaco.languages.setMonarchTokensProvider(languageId, tokensProvider)
– Set a syntax highlighting (tokens) provider for a language. Monaco uses Monarch (a declarative lexical analyzer) for defining syntax. You provide a tokensProvider with a tokenizer rules to enable highlighting for your custom language. (Not needed for built-in languages—they already have tokens providers.)- Language Feature Providers: Monaco has a range of
registerXProvider
functions to add features:registerCompletionItemProvider(langId, provider)
– Add autocompletion for a language. Theprovider
must have aprovideCompletionItems(model, position, context, token)
function returning a list of suggestions. You can specifytriggerCharacters
on the provider to automatically trigger on certain characters (e.g..
).registerHoverProvider(langId, provider)
– Provide hover tooltips. The provider’sprovideHover(model, position)
should return a Hover object (with contents as Markdown or plain text, and a range).registerSignatureHelpProvider(langId, provider)
– Provide function signature help (parameter info). You define when to trigger (e.g. on(
) and what info to show.registerDefinitionProvider(langId, provider)
– Enable "Go to Definition". The provider returns the location of a symbol’s definition given a position.registerReferenceProvider
,registerDocumentSymbolProvider
,registerRenameProvider
, etc. – Similar pattern for references, outline (symbols), rename, etc.registerInlineCompletionsProvider(langId, provider)
– Provide inline ghost-text completions. The provider returns completion text to display at the cursor (see Inline Completions above).registerCodeActionProvider(langId, provider)
– Provide code actions (quick fixes).registerCodeLensProvider(langId, provider)
– Provide CodeLens items.registerColorProvider(langId, provider)
– Provide color pickers and color decorators if your language has color codes.- … and others for formatting, folding, etc.
monaco.languages.CompletionItemKind
– An enum of suggestion item types (likeKind.Function
,Kind.Snippet
, etc.) that you can use when creating completion items to show appropriate icons.monaco.languages.SnippetString
– A utility to define snippet text with tab stops, etc., if you want to return a snippet completion.monaco.languages.typescript
/monaco.languages.css
/ etc. – Monaco exposes some configuration for built-in languages under sub-namespaces, for example you can configure the TypeScript compiler options viamonaco.languages.typescript.javascriptDefaults
(this gets into advanced usage like providing custom typings or compiler options to the TS language service).
In essence, monaco.languages
is where you hook in to extend or customize what happens when a user interacts with code in a certain language. If you are using Monaco purely with built-in language support, you might not call these at all (except perhaps to add a completion or two). But if you want to do something like add a special autocomplete for a keyword, or implement a new language, these APIs are essential.
This is an enumeration used with diagnostics/markers to indicate the severity level of an issue:
monaco.MarkerSeverity.Error
– High severity, typically shown with a red squiggly and a stop icon. Indicates an error that likely prevents execution or compilation.monaco.MarkerSeverity.Warning
– Shown with a yellow/orange squiggly and warning icon. Indicates a potential issue but not necessarily a fatal error.monaco.MarkerSeverity.Info
– Shown with a blue squiggly (information icon). Used for informational messages.monaco.MarkerSeverity.Hint
– Shown with a faint dotted underline or other subtle indicator (light bulb hint icon). Often used for refactoring suggestions or very low priority notifications.
When creating marker data for setModelMarkers
, you assign one of these severities. They help the user distinguish the importance of diagnostics. Monaco may style each differently (e.g., red vs yellow underline). The numeric values behind these (if curious) are typically Error = 8, Warning = 4, Info = 2, Hint = 1, but you should just use the constants.
Monaco includes a Uri
class for identifying models and resources. This is essentially a URL/URI representation:
- You can create a Uri via
monaco.Uri.parse("file:///path/to/file.txt")
or use helper likemonaco.Uri.file("filename.ts")
. For browser, these URIs need not correspond to real files; they are just unique identifiers. - When you call
monaco.editor.createModel
, if you provide auri
, Monaco will internally index the model by that URI. This is useful if you want to later retrieve that model or if you want Monaco’s language service to recognize files. For example, the TypeScript language service running in Monaco treats models with afile://
URI as if they were files, and can provide cross-file analysis if multiple models have URIs, etc. - If you don't provide a URI, Monaco will auto-generate one (with a
inmemory://
scheme). monaco.Uri
has properties likescheme
,path
etc., and methods liketoString()
. Typically, you might useUri.file("foo.js")
for a model so that it ends with.js
and Monaco knows to treat it as JavaScript (if you didn't explicitly set the language).- When working with markers, the marker objects returned from
monaco.editor.getModelMarkers()
will include aresource
property which is aUri
pointing to the model the marker is on. You can compare or checkmarker.resource.path
or.toString()
to identify the file.
In short, monaco.Uri
is how Monaco identifies and organizes models akin to files in an editor. They are especially important if you have multiple files open or if you want to implement features like import references across files.
These are utility enums to define keybindings for commands/actions:
- monaco.KeyCode: An enum of keyboard keys and special keys. For example:
KeyA
,KeyB
, ...KeyZ
for letters,Digit0
–Digit9
for number keys,Enter
,Tab
,Escape
,Space
,Backspace
, etc. for controls,F1
–F12
for function keys,ArrowUp
,ArrowDown
, etc. for arrows, and so on.- There are also codes for punctuation (e.g.
US_SEMICOLON
,US_EQUAL
, etc., which correspond to specific key codes).
- monaco.KeyMod: Flags for modifier keys:
.CtrlCmd
– Represents "Ctrl" on Windows/Linux and "Command (⌘)" on Mac..Shift
– The Shift key..Alt
– The Alt key (Option on Mac)..WinCtrl
– The Windows key (or Ctrl on Mac, in some contexts).
To create a keybinding, combine the modifiers with a key code using bitwise OR (|
). Some examples:
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S
– Ctrl+S (or Command+S on Mac).monaco.KeyMod.Shift | monaco.KeyMod.Alt | monaco.KeyCode.KEYF
– Shift+Alt+F.monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEYP
– Ctrl+Shift+P (which in VSCode opens command palette).monaco.KeyCode.F9
– Just F9 (often used for toggle breakpoint, for instance).
When you use these in editor.addCommand
or in an action’s keybindings
array, Monaco will register that key combo to trigger your command. Note that Monaco automatically handles differences in Mac/Windows for CtrlCmd. If you want a key exclusively for one platform, you might have to conditionally use KeyMod.WinCtrl
vs CtrlCmd
.
Now that we’ve covered the basics, let's walk through some practical examples (in Svelte) to solidify how to use Monaco Editor’s features. Each recipe demonstrates a common use-case with code. These examples assume you have the MonacoEditor.svelte
component (from Getting Started) or a similar setup, and have access to the monaco
object (either via the loader or global).
This recipe shows the simplest way to embed an editor in a Svelte component.
Explanation: We will create an editor with some initial code and basic options.
Steps:
- Use the
MonacoEditor.svelte
component as a child, or directly create an editor in your Svelte file on mount. - Provide initial content and language.
Code Example (inline setup in a Svelte component):
<script>
import { onMount } from 'svelte';
import loader from '@monaco-editor/loader'; // Monaco loader
let editorContainer;
let editor;
let monaco;
onMount(async () => {
monaco = await loader.init();
editor = monaco.editor.create(editorContainer, {
value: '<h1>Hello Monaco!</h1>\n<p>Edit some code...</p>',
language: 'html', // use HTML language for syntax highlighting
theme: 'vs', // light theme
automaticLayout: true // adjust editor layout when container resizes
});
});
</script>
<div bind:this={editorContainer} style="height:300px; border:1px solid #ccc;"></div>
When this component mounts, an editor appears inside the div, showing the HTML code with proper syntax coloring. The automaticLayout: true
option makes the editor respond to container size changes (useful if your layout is fluid). We set a border just to visually separate the editor area.
This recipe demonstrates two-way binding of the editor content with a Svelte store. We’ll keep the editor text in sync with a Svelte state, so that changes in one reflect in the other.
Explanation: Svelte can’t automatically bind to Monaco’s content, but we can achieve it by hooking into Monaco’s change events and store subscriptions.
Steps:
- Create a Svelte writable store to hold the editor text.
- Initialize the Monaco editor with the store’s current value.
- On editor content change, update the store.
- Subscribe to the store and update the editor if the value was changed externally.
Code Example:
<script>
import { writable } from 'svelte/store';
import { onMount } from 'svelte';
import loader from '@monaco-editor/loader';
// A writable store for the content
export const contentStore = writable("function greet() {\n console.log('Hello');\n}\n");
let editor, monaco;
let editorContainer;
onMount(async () => {
monaco = await loader.init();
// Create a model with the store's initial value
const model = monaco.editor.createModel(
$contentStore,
'javascript',
monaco.Uri.parse('file:///script.js')
);
editor = monaco.editor.create(editorContainer, {
model: model,
language: 'javascript',
theme: 'vs-light'
});
// Update store when editor content changes
editor.onDidChangeModelContent(() => {
contentStore.set(editor.getValue());
});
// Update editor if store changes (e.g., from outside this component)
const unsubscribe = contentStore.subscribe((value) => {
if (value !== editor.getValue()) {
// Avoid recursive updates by checking current value
editor.setValue(value);
}
});
});
</script>
<div bind:this={editorContainer} style="height:250px;"></div>
In this code:
- We initialize the model with
$contentStore
(the store’s current value) and specify the language. - Whenever the editor’s content changes (
onDidChangeModelContent
fires), we write the new value to the Svelte store. - We subscribe to the
contentStore
; if an external change occurs (e.g., another component or a reset button modifies the store), we set the editor’s value accordingly. - We gave the model a URI
"file:///script.js"
mainly to illustrate usage ofmonaco.Uri
– it’s not strictly needed here but could help if Monaco’s JS language service wants to treat it as a file.
Now the editor and the store are synced. For example, if you had an <input bind:value={$contentStore}>
elsewhere, typing in that input would update the editor, and typing in the editor would update the input.
Suppose you want to highlight all occurrences of a certain word or phrase in the editor (like a simple search feature). You can use Monaco’s decorations for this.
Explanation: We will find all instances of a query string in the editor text and add a yellow highlight behind those words using a decoration.
Steps:
- Compute all ranges in the model that match the search query.
- Use
editor.deltaDecorations
to add decorations for those ranges with a specific CSS class or style. - The CSS for the decoration will set a background color to highlight the text.
Code Example:
<script>
import { onMount } from 'svelte';
import loader from '@monaco-editor/loader';
let editor, monaco;
let editorContainer;
let highlightDecorations = []; // to track decoration IDs
const searchTerm = 'Monaco'; // The term to highlight (case-sensitive in this simple example)
onMount(async () => {
monaco = await loader.init();
editor = monaco.editor.create(editorContainer, {
value:
'Monaco is a great editor. Monaco provides many features.\nFeel free to explore Monaco!',
language: 'plaintext',
theme: 'vs-light'
});
highlightMatches(); // call initially if needed
});
function highlightMatches() {
if (!editor) return;
const model = editor.getModel();
const text = model.getValue();
const term = searchTerm;
const newDecorations = [];
if (term) {
let startIndex = 0;
while (true) {
const idx = text.indexOf(term, startIndex);
if (idx === -1) break;
// Convert index to line/column position
const startPos = model.getPositionAt(idx);
const endPos = model.getPositionAt(idx + term.length);
newDecorations.push({
range: new monaco.Range(
startPos.lineNumber,
startPos.column,
endPos.lineNumber,
endPos.column
),
options: {
inlineClassName: 'myHighlight' // CSS class to apply
}
});
startIndex = idx + term.length;
}
}
// Apply decorations, keep track of new decorations' IDs
highlightDecorations = editor.deltaDecorations(highlightDecorations, newDecorations);
}
</script>
<div bind:this={editorContainer} style="height:200px;"></div>
<style>
/* Define the highlight style for the decoration */
.myHighlight {
background-color: yellow;
color: black; /* optional: override text color for contrast */
}
</style>
In this snippet:
- We search for
"Monaco"
in the text. For each occurrence, we determine its position in terms of line and column usingmodel.getPositionAt(index)
. - We create a
monaco.Range
for that occurrence and specify aninlineClassName
for the decoration options. Monaco will apply a<span>
with that class around the text in the range. - We call
editor.deltaDecorations(oldDecorations, newDecorations)
. The first argument is the array of previous decoration IDs we want to replace. Initially it’s empty, later calls will pass the last returned IDs to remove old highlights before adding new ones. - The CSS
.myHighlight
is defined in the<style>
of the component to give a yellow background.
This approach can be tied to an input field for dynamic searching: whenever the search query changes, update searchTerm
and call highlightMatches()
to refresh decorations.
Here we demonstrate providing a simple inline completion (ghost text) to the editor. For instance, whenever the user types the word "hello"
, we’ll suggest completing it to "hello world"
as ghost text.
Explanation: We will enable inline suggestions and register a custom inline completions provider for a specific trigger.
Steps:
- Enable inline suggestions in the editor options (
inlineSuggest.enabled: true
). - Register an inline completions provider on
monaco.languages
for the language in use (we’ll use plaintext for simplicity). - In the provider’s
provideInlineCompletions
, detect if the text before cursor matches our trigger (e.g. "hello") and if so, return a suggested completion.
Code Example:
<script>
import { onMount } from 'svelte';
import loader from '@monaco-editor/loader';
let editor, monaco;
let editorContainer;
onMount(async () => {
monaco = await loader.init();
// Register inline completions provider for plaintext
monaco.languages.registerInlineCompletionsProvider('plaintext', {
provideInlineCompletions(model, position, context, token) {
const line = model.getLineContent(position.lineNumber);
const textBeforeCursor = line.substring(0, position.column - 1);
// Simple trigger: if textBeforeCursor ends with "hello", provide a completion
if (/\bhello$/.test(textBeforeCursor)) {
return {
items: [
{
insertText: ' world', // text to insert at cursor
range: new monaco.Range(
position.lineNumber,
position.column,
position.lineNumber,
position.column
),
command: null
}
]
};
}
return { items: [] }; // no suggestion otherwise
},
// Optionally, you can implement handle to free resources, but not needed here
freeInlineCompletions() {}
});
// Create editor with inline suggestions enabled
editor = monaco.editor.create(editorContainer, {
value: 'Say hello',
language: 'plaintext',
inlineSuggest: { enabled: true }
});
});
</script>
<div bind:this={editorContainer} style="height:100px; border:1px solid #888;"></div>
How this works:
- We register an inline completions provider for
'plaintext'
language. TheprovideInlineCompletions
function checks if the word "hello" is right before the cursor. If yes, it returns an item suggesting " world". - The returned item specifies
insertText: ' world'
and arange
which is just an empty range at the cursor (so the text will be inserted at the cursor position). - We enable inline suggestions on the editor. Now, when you type "hello", as soon as you hit space (triggering the regex
\bhello
at end of word), the editor should show a ghost text " world" after the cursor. Pressing Tab (or right arrow) should accept it, inserting " world" into the document. - This is a trivial example. In practice, you might call an AI API or have a more complex logic to provide inline completions. But the mechanism is the same.
In this recipe, we'll add a custom autocomplete suggestion to an existing language. For demonstration, whenever we type console.
in a JavaScript editor, we’ll include a special suggestion for console.log()
even though Monaco already suggests it – just to show how to plug into the suggestions.
Explanation: Use monaco.languages.registerCompletionItemProvider
for the language. Provide a list of suggestions when triggered (either by certain characters or on general request).
Steps:
- Register a completion item provider for JavaScript.
- Use a
triggerCharacters
array if you want to automatically trigger on certain characters (like.
). - In
provideCompletionItems
, inspect the context or the prefix (text near the cursor) to decide what suggestions to return. - Return an object with a
suggestions
array. Each suggestion needs at least alabel
andinsertText
, plus akind
and possiblydetail
ordocumentation
.
Code Example:
<script>
import { onMount } from 'svelte';
import loader from '@monaco-editor/loader';
let editor, monaco;
let editorContainer;
onMount(async () => {
monaco = await loader.init();
// Register a completion item provider for JavaScript
monaco.languages.registerCompletionItemProvider('javascript', {
triggerCharacters: ['.'], // trigger completions when user types a dot
provideCompletionItems(model, position, context, token) {
const wordInfo = model.getWordUntilPosition(position);
const wordUntil = wordInfo.word; // the partially typed word
const textBefore = model
.getLineContent(position.lineNumber)
.substring(0, position.column - 1);
// We will always add a suggestion for console.log when 'console.' is typed
const suggestions = [];
if (textBefore.endsWith('console.')) {
suggestions.push({
label: 'log()',
kind: monaco.languages.CompletionItemKind.Function,
insertText: 'log(${1:object})',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
detail: 'Log output to console',
documentation: 'Insert a console.log statement'
});
}
return { suggestions };
}
});
editor = monaco.editor.create(editorContainer, {
value: 'console.',
language: 'javascript',
theme: 'vs-dark'
});
});
</script>
<div bind:this={editorContainer} style="height:150px;"></div>
What happens here:
- We specify
triggerCharacters: ['.']
. This means whenever a.
is typed, Monaco will invoke ourprovideCompletionItems
. - In
provideCompletionItems
, we check if the text before the cursor ends with"console."
. If yes, we create a suggestion object forlog()
. - The suggestion object:
label
: what's shown in the suggestion list.kind
: the type of item (using Monaco’s enum for an icon, here we choose Function kind).insertText
: the text to insert. We provided"log(${1:object})"
, which includes a snippet placeholder${1:object}
. This means if our suggestion is accepted, it will insertlog()
with a snippet such that the cursor is inside parentheses withobject
highlighted (the user can tab to jump out of the snippet).insertTextRules
: we set it toInsertAsSnippet
because our insert text has snippet syntax.detail
anddocumentation
: additional info shown in the suggestions UI.
- Now in the editor, if you type
console.
you should see a suggestion forlog()
(among the default suggestions). Selecting it will insertlog(object)
withobject
selected, ready to edit.
This pattern can be used to provide an entire list of domain-specific completions (for example, a list of keywords or API calls in a custom language). Just push more items into the suggestions
array.
To display an error or warning in the editor (with squiggly underline and message), you use markers (diagnostics). In this recipe, we will artificially mark the word "TODO" as a warning in the editor content.
Explanation: Iterate through the model to find a pattern and mark each occurrence with a warning marker.
Steps:
- Find positions of "TODO" in the text (or any condition for a warning).
- Create marker objects for each, with severity Warning and a message.
- Call
monaco.editor.setModelMarkers
to set these markers.
Code Example:
<script>
import { onMount } from 'svelte';
import loader from '@monaco-editor/loader';
let editor, monaco;
let editorContainer;
const warningWord = 'TODO';
onMount(async () => {
monaco = await loader.init();
editor = monaco.editor.create(editorContainer, {
value:
'// TODO: fix the logic below\nfunction calc(x) {\n return x * 2; // TODO might need refine\n}',
language: 'javascript',
theme: 'vs-light'
});
markTodosAsWarning();
});
function markTodosAsWarning() {
const model = editor.getModel();
const text = model.getValue();
const markers = [];
let idx = 0;
while ((idx = text.indexOf(warningWord, idx)) !== -1) {
const startPos = model.getPositionAt(idx);
const endPos = model.getPositionAt(idx + warningWord.length);
markers.push({
startLineNumber: startPos.lineNumber,
startColumn: startPos.column,
endLineNumber: endPos.lineNumber,
endColumn: endPos.column,
message: `"${warningWord}" found: Remember to address this!`,
severity: monaco.MarkerSeverity.Warning
});
idx += warningWord.length;
}
monaco.editor.setModelMarkers(model, 'myOwner', markers);
}
</script>
<div bind:this={editorContainer} style="height:180px;"></div>
In this example:
- Our initial code has two occurrences of "TODO".
markTodosAsWarning
goes through the text and for each "TODO", creates a marker with a message andMarkerSeverity.Warning
.- We then call
setModelMarkers
. The "owner" here is just an identifier string ("myOwner") – you can use any string to group your markers, e.g., the name of your feature or extension. - After running this, Monaco will render yellow squiggly lines under each "TODO". Hovering over it will show the warning message "TODO found: Remember to address this!" and the gutter will show a warning triangle icon on those lines.
If the text changes (e.g., user edits), you’d want to recompute and set markers again (possibly debounced). Markers are not dynamic by themselves; they represent a snapshot of issues at the time you set them.
Let’s add a simple CodeLens to our code. For demonstration, above every function definition in JavaScript, we’ll show a CodeLens that says "Run Function" and when clicked, it will alert()
the name of the function.
Explanation: Register a CodeLens provider that scans for function definitions and returns lenses. Also, register a command to handle clicks on the CodeLens.
Steps:
- Register a CodeLens provider for JavaScript.
- In
provideCodeLenses
, use a regex or simple text scan to find lines starting with "function". - For each, create a CodeLens item with a range (the line of the function) and assign a command with an id and title.
- Use the editor instance to add a command with that id which runs the desired action.
- The CodeLens provider returns all the lenses. Optionally implement
resolveCodeLens
if you need lazy loading (not needed here since we have all info).
Code Example:
<script>
import { onMount } from 'svelte';
import loader from '@monaco-editor/loader';
let editor, monaco;
let editorContainer;
let runFunctionCommandId;
onMount(async () => {
monaco = await loader.init();
editor = monaco.editor.create(editorContainer, {
value:
'function greet(name) {\n return `Hello, ${name}`;\n}\n\nfunction add(a, b) {\n return a + b;\n}',
language: 'javascript',
theme: 'vs-light'
});
// Register a command that will be invoked when CodeLens is clicked
runFunctionCommandId = editor.addCommand(0, (ctx, ...args) => {
// args[0] can be function name passed from CodeLens
const funcName = args[0];
alert(`Run Function: ${funcName}()`);
});
// Register CodeLens provider
monaco.languages.registerCodeLensProvider('javascript', {
provideCodeLenses(model) {
const lenses = [];
const lines = model.getLinesContent();
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const match = line.match(/^function\s+(\w+)\s*\(/);
if (match) {
const name = match[1];
lenses.push({
range: {
startLineNumber: i + 1,
startColumn: 1,
endLineNumber: i + 1,
endColumn: 1
},
command: {
id: runFunctionCommandId,
title: `Run Function "${name}"`,
arguments: [name]
}
});
}
}
return { lenses, dispose: () => {} };
},
resolveCodeLens(model, codeLens) {
// Not needed to resolve in this simple case
return codeLens;
}
});
});
</script>
<div bind:this={editorContainer} style="height:220px;"></div>
Here’s what’s happening:
- We create the editor with some sample functions.
- We call
editor.addCommand
with0
(no keybinding for this command, it's just to be invoked via CodeLens click). The handler function uses an argument (the function name) to alert which function was "run". We store the returned command id inrunFunctionCommandId
. - We register a CodeLens provider on 'javascript':
- In
provideCodeLenses
, we loop through each line of the model. We check if the line starts withfunction name(
using a regex. If yes, we capture the function name. - For each such line, we create a CodeLens item:
- The
range
is from column 1 to 1 on that line (essentially, we place the lens at the start of the line). - The
command
for the lens uses therunFunctionCommandId
we created, with a title likeRun Function "greet"
and we pass the function name as an argument.
- The
- We return all lenses. The
dispose
is just an empty function (not strictly needed). - We implement
resolveCodeLens
trivially by returning the lens; in complex scenarios, you might only create unresolved lenses and then resolve (e.g., to fetch data asynchronously).
- In
- Monaco will display the CodeLens text above each function line. The title shows the function name.
- When clicked, Monaco will invoke the command associated. Our command handler then alerts the function name.
This pattern can be used for more meaningful tasks – for example, "Run Test" above each test function, which when clicked could execute the test in some environment or send a message. Remember that the command is running in the context of the webpage, so it could call any JS function, make network requests, etc.
In this final recipe, we show how to add a custom keyboard shortcut to the editor. We’ll implement a simple Ctrl+S (or Cmd+S on Mac) to show a message, since by default Monaco (unlike VS Code) doesn’t have a save command bound.
Explanation: Use editor.addAction
or editor.addCommand
to bind a key. We will use addAction
here to also give it a name.
Steps:
- After creating the editor, call
editor.addAction
with an object describing the action:- Provide an
id
(unique string), alabel
(for menu/tooltip), and akeybindings
array with the desired key combo. - Provide a
run
function that will execute when the key is pressed.
- Provide an
- That’s it. The action is now active when the editor is focused.
Code Example:
<script>
import { onMount } from 'svelte';
import loader from '@monaco-editor/loader';
let editor, monaco;
let editorContainer;
onMount(async () => {
monaco = await loader.init();
editor = monaco.editor.create(editorContainer, {
value: 'function example() {\n // Press Ctrl+S to trigger action\n}\n',
language: 'javascript'
});
// Add a custom action for Ctrl+S (save)
editor.addAction({
id: 'save-command',
label: 'Save (Ctrl+S)',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S],
precondition: null,
keybindingContext: null,
run: (ed) => {
const content = ed.getValue();
console.log('Saving content... (content length ' + content.length + ')');
alert('Custom Save Triggered!');
}
});
});
</script>
<div bind:this={editorContainer} style="height:120px;"></div>
Details:
- We specify
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S
as the keybinding, which covers Ctrl+S / Cmd+S. - The
run
function here simply logs the content length and shows an alert. In a real app, you might send the content to a backend or download a file, etc. precondition
andkeybindingContext
are advanced features that let you enable the action only under certain conditions (like when a certain editor context key is true). We set them to null to always allow.- The
label
"Save (Ctrl+S)" might appear in the editor’s context menu or command palette (Monaco has a basic command palette opened by F1).
Now, focusing the editor and pressing Ctrl+S will run our custom save logic. (By default, Monaco does nothing on Ctrl+S, it doesn’t intercept it unless an action is bound, which is why this works without unbinding anything).
These examples illustrate how to use Monaco Editor in a Svelte environment without relying on bundler-specific setup. By loading Monaco from a CDN and using the provided API, you can create a powerful code editing interface in the browser with Svelte for reactivity and structure. Feel free to mix and match these techniques—Monaco is very flexible and can be tailored to fit a variety of use cases from simple code snippets to full-fledged IDE features. Happy coding!
Monaco Editor is the code editor engine that powers VS Code, offering a rich API to embed code editing in web applications. This reference is organized by common use cases rather than by API namespace, to help developers quickly find relevant classes, interfaces, and functions. Each API item below includes a brief description of its purpose and typical usage, based on the official Monaco documentation.
-
monaco.editor.create(container: HTMLElement, [options])
– Creates a new standalone code editor in the given empty DOM element. You can pass anIStandaloneEditorConstructionOptions
to configure settings (like initial language, theme, etc.). Returns anIStandaloneCodeEditor
instance representing the editor (create | Monaco Editor API). -
monaco.editor.createDiffEditor(container: HTMLElement, [options])
– Creates a new diff editor in the given DOM element for side-by-side comparison of two text models. AcceptsIStandaloneDiffEditorConstructionOptions
for configuration and returns anIStandaloneDiffEditor
instance (createDiffEditor | Monaco Editor API). (A diff editor shows two editors for original and modified text and highlights differences.) -
IStandaloneCodeEditor
– The interface for a standalone code editor instance (frommonaco.editor.create
). It extends the baseICodeEditor
and provides methods to read or manipulate the editor’s content and state, such asgetModel()
,getValue()
,setValue()
,updateOptions()
, etc., as well as events likeonDidChangeModelContent
(fires on text change) (IStandaloneCodeEditor | Monaco Editor API). UseIStandaloneCodeEditor.dispose()
to destroy an editor when done. -
IStandaloneDiffEditor
– The interface for a standalone diff editor instance (fromcreateDiffEditor
). It allows access to the two inner code editors (getOriginalEditor()
andgetModifiedEditor()
) and diff-specific functionality. For example,IStandaloneDiffEditor.getLineChanges()
retrieves the list of line differences (IStandaloneDiffEditor | Monaco Editor API) (IStandaloneDiffEditor | Monaco Editor API). It also providesonDidUpdateDiff
event and navigation methods (e.g.revealFirstDiff()
). -
IEditorConstructionOptions
/IStandaloneEditorConstructionOptions
– Configuration objects for creating editors. These allow you to specify initial settings like the language, theme, read-only mode, line numbers visibility, etc., at creation time (create | Monaco Editor API). For diff editors, useIStandaloneDiffEditorConstructionOptions
. (Many appearance options can also be changed later viaupdateOptions()
.) -
Managing Editor Lifecycle – Each editor instance should be disposed when no longer needed: call
editor.dispose()
to free resources. Multiple editors can share the same text model (document) or have separate models. You can retrieve all current editor instances withmonaco.editor.getEditors()
and all models withmonaco.editor.getModels()
(editor | Monaco Editor API). The global eventmonaco.editor.onDidCreateEditor
fires after any new editor is created (editor | Monaco Editor API). -
Attaching and Changing Models – When creating an editor, you can optionally supply a text model (via the
model
field in options) or one will be created by default. Editors can switch documents: useeditor.setModel(newModel)
to attach a differentITextModel
to an editor (IStandaloneCodeEditor | Monaco Editor API). You can even calleditor.setModel(null)
to detach a model (making an editor empty) (IStandaloneCodeEditor | Monaco Editor API). (If an editor was created with its own new model and you detach it, Monaco will dispose that model; if you attached an existing model viasetModel
, it will not be disposed.) -
Resizing and Layout – If the editor’s container size changes (e.g. on window resize or pane toggle), call
editor.layout()
to update the editor’s dimensions. You can pass in an{ width, height }
object to explicitly set size viaeditor.layout({ width: 800, height: 600 })
. This ensures the editor redraws correctly.
-
Cursor Position and Selection – The editor exposes and accepts positions and selections for programmatic navigation.
editor.getPosition()
returns the current cursor position (Position
withlineNumber
andcolumn
), andeditor.setPosition({ lineNumber, column })
moves the cursor (IStandaloneCodeEditor | Monaco Editor API). Similarly,editor.getSelection()
yields the current text selection (Selection
which is a subclass ofRange
), andeditor.setSelection(range)
selects that range. Multi-cursor is supported:editor.getSelections()
returns all selection ranges if multiple cursors exist, andeditor.setSelections(arrayOfSelections)
can define multiple selections at once (IStandaloneDiffEditor | Monaco Editor API). TheSelection
object also tracks the direction of selection (but in most cases you can treat it like a range). -
Revealing and Scrolling – To programmatically navigate or reveal text, use methods like
editor.revealLine(lineNumber)
oreditor.revealPosition(position)
. These scroll the editor to bring the given line or position into view (with options to center it, etc.) (IStandaloneDiffEditor | Monaco Editor API). For example,editor.revealLineInCenter(42)
will scroll so that line 42 is centered in the viewport. There are also variants such asrevealRange(range)
and methods to scroll without animation. -
Editing Content Programmatically – You can insert, replace, or delete text in the editor through the model or editor APIs. A convenient way is using
editor.executeEdits(source, edits)
to apply an array of text edits to the current model (IStandaloneCodeEditor | Monaco Editor API). Each edit is a{ range: Range, text: string }
(with an optionalforceMoveMarkers
flag), and all edits will be applied as a single undo step. For example, to insert text at the cursor you could useexecuteEdits
with an edit where range is the empty selection at the cursor. This method returns a boolean indicating if the operation was applied. Alternatively, you can manipulate the text model directly (see Working with Models below) usingmodel.pushEditOperations
ormodel.applyEdits
. -
Reading Content – To get the editor’s text, you typically call
editor.getModel().getValue()
which returns the full document text. You can also get text in a specific range viamodel.getValueInRange(range)
(ITextModel | Monaco Editor API). If you need the text with line endings preserved or specific line content, use model methods (e.g.model.getLineContent(lineNumber)
). There is also aneditor.getValue()
convenience that returns the full text of the attached model. -
Undo/Redo – Monaco manages undo/redo per model. Programmatically, you can invoke undo or redo via command IDs (see Commands section) or by
editor.trigger('keyboard', 'undo', null)
for example. There are no directeditor.undo()
methods; instead, use the built-in commands. -
Find and Replace – While Monaco has a built-in find widget UI, you can use the model’s search APIs for custom find/replace functionality. For example,
model.findMatches(searchString, ...)
returns an array ofFindMatch
(with range and matches) (ITextModel | Monaco Editor API). You could use this to highlight occurrences (via decorations) or implement your own search interface. Replacement can be done via applying edits to those ranges. -
Editor Events (Typing & Navigation) – Editors emit events to let you react to user edits or cursor moves. Notably,
editor.onDidChangeModelContent
fires after any text change in the model (with details about the changes) (ITextModel | Monaco Editor API).editor.onDidChangeCursorPosition
fires when the cursor position changes (e.g. arrow keys or mouse click) (IStandaloneCodeEditor | Monaco Editor API), andeditor.onDidChangeCursorSelection
fires when the selection changes (IStandaloneCodeEditor | Monaco Editor API) (e.g. user selects text). You can subscribe to these via, for example:editor.onDidChangeCursorSelection(evt => { ... })
. These events provide information like the new position/selection and the reason for the change. -
Focus and Keyboard – You can programmatically focus the editor with
editor.focus()
. If needed, you can simulate key or command presses:editor.trigger(source, commandId, payload)
will trigger an editor command or action by its identifier (IStandaloneCodeEditor | Monaco Editor API). For instance,editor.trigger('api', 'type', { text: 'hello' })
would insert the text "hello" at the cursor as if typed. Common built-in command IDs includecursorUndo
,undo
,redo
,editor.action.commentLine
, etc. (See VS Code’s editor command IDs for reference.)
-
monaco.editor.createModel(text: string, [language], [uri])
– Creates a new text model (document) with the given text content. Optionally, you can specify a language identifier for syntax highlighting; if not, Monaco may infer it from the file name extension of the provided URI (createModel | Monaco Editor API). You can also pass amonaco.Uri
to assign a unique URI to the model (if no URI is provided, an internal one is generated). The returnedITextModel
represents the in-memory document. -
monaco.editor.getModels()
– Returns an array of all current text models in memory (editor | Monaco Editor API). Each model is anITextModel
(which is often the same object type as used in editors). You can use this to iterate or search through all documents loaded. -
monaco.editor.getModel(uri)
– Retrieves theITextModel
with the given URI, if it exists (editor | Monaco Editor API). URIs are used as unique identifiers for models (for example, if you open multiple files, each gets a distinctUri
). This is useful if you have the URI and need the model instance. -
Model Interface (
ITextModel
) – Represents a text document’s data and provides many methods to work with the content. For example,model.getValue()
gets the full text, andmodel.setValue(newText)
replaces the entire content (ITextModel | Monaco Editor API). To modify partially,model.applyEdits(edits)
applies an array ofIIdentifiedSingleEditOperation
(range edits) to the model (ITextModel | Monaco Editor API). You can also usepushEditOperations
for a lower-level way to apply edits with custom selection logic. The model tracks undo/redo history for those edits. Other useful methods include:model.getLineCount()
,model.getLineContent(lineNumber)
,model.findMatches(pattern, ...)
for search (ITextModel | Monaco Editor API), andmodel.validatePosition(pos)
/validateRange(rng)
to adjust out-of-bound positions (ITextModel | Monaco Editor API). -
Model Options – Models can be configured with certain options independent of the editor, such as tab size and indent settings. Use
model.updateOptions({ tabSize: 4, insertSpaces: true })
to set indentation preferences on a model (ITextModel | Monaco Editor API). (By default, models may inherit some settings from the editor that created them, but you can override per model.) -
Language Association – Each model knows its language. You can retrieve it with
model.getLanguageId()
. To change a model’s language (e.g. user selects a different language mode for the file), usemonaco.editor.setModelLanguage(model, languageId)
which will reconfigure the model to the new language rules (setModelLanguage | Monaco Editor API). This will trigger a re-tokenization and any language-specific features (like completions or formatting) to update for that model. -
Attaching Models to Editors – A model can be shown in an editor via
editor.setModel(model)
. Multiple editors can share the same model (all will reflect the same content and changes). This is how the diff editor works internally (two editors, each with their own model). When you no longer need a model (e.g. file closed), you canmodel.dispose()
to free it; this will emit theonWillDispose
event and remove it fromgetModels()
. -
Model Events – The
ITextModel
emits events for content and language changes.model.onDidChangeContent
fires after each editing operation with details like the ranges inserted or deleted (ITextModel | Monaco Editor API).model.onDidChangeLanguage
fires if the model’s language is changed (viasetModelLanguage
) (ITextModel | Monaco Editor API). There are also events for model options changes, for example if indent settings change (onDidChangeOptions
). If a model is bound to an editor, the editor will reflect these changes automatically. -
Model Lifecycle Events – Monaco also provides global events for model creation and destruction.
monaco.editor.onDidCreateModel
is emitted when any new model is created (for example viacreateModel
) (editor | Monaco Editor API).monaco.editor.onWillDisposeModel
is emitted right before a model is disposed (useful to clean up any of your own state linked to that model) (editor | Monaco Editor API). These events deliver the model involved as part of the event data. -
Finding and Replacing Text – Models provide rich search capabilities. Use
model.findMatches(query, …)
to find all occurrences of a string or regex in the document (ITextModel | Monaco Editor API). It returns an array ofFindMatch
objects, each containing arange
and the matchingmatches
strings. There are alsofindNextMatch
andfindPreviousMatch
to find the next/previous occurrence from a position (ITextModel | Monaco Editor API). Monaco does not have a built-in replace all API call, but you can combine findMatches with applyEdits to perform a replace across the model. -
Markers vs. Model Decorations – Note that error markers (diagnostics) are not stored in the model content; they are managed via the marker API (see Diagnostics and Markers). Separately, editor decorations (highlights) can be associated with a model. A model has methods like
model.deltaDecorations(oldDecorations, newDecorations)
to add or remove decorations on that model (ITextModel | Monaco Editor API). Typically, you will use the editor methods for decorations, but in scenarios where a model isn’t attached to an editor, you can decorate it directly (e.g. for off-screen text analysis).
Monaco’s language support is extensible. The monaco.languages
namespace lets you register providers for various language smart-features (much like VS Code extensions). Below are key language feature hooks you can use:
-
Registering a Language – Before using language features, a language must be known to Monaco. Use
monaco.languages.register({ id: 'myLang', ... })
to register a new language by an identifier (languages | Monaco Editor API). The object can include optional properties likeextensions
,aliases
, ormimetypes
to help Monaco infer language from filenames. This does not itself add any highlighting or logic, it just declares the language. -
Language Configuration – Use
monaco.languages.setLanguageConfiguration(langId, configuration)
to define the syntax basics for a language: brackets (for auto-bracket completion), comment symbols, indentation rules, auto-closing pairs, surrounding pairs, etc. ThisLanguageConfiguration
tells the editor how to auto-indent or toggle comments for the language (languages | Monaco Editor API). -
Tokenization (Syntax Highlighting) – Monaco supports two ways to define syntax highlighting for a language:
- Monarch Tokens Provider:
monaco.languages.setMonarchTokensProvider(langId, monarchDefinitions)
– This uses Monaco’s built-in Monarch DSL to describe lexical tokens with regex patterns and styles (languages | Monaco Editor API). You provide a JSON-like description of tokens (keywords, comments, strings, etc.) and their scopes. - Standalone Tokens Provider:
monaco.languages.setTokensProvider(langId, provider)
– This allows you to implement theTokensProvider
interface for complete control over tokenization (languages | Monaco Editor API). Typically, Monarch is simpler for custom languages, while a TokensProvider might wrap an existing parser or WASM.
- Monarch Tokens Provider:
-
Auto-Completion (IntelliSense) – Use
monaco.languages.registerCompletionItemProvider(langId, provider)
to register a completion provider (registerCompletionItemProvider | Monaco Editor API). The provider must implementprovideCompletionItems(model, position, context, token)
and return a list of suggestions (aCompletionList
ofCompletionItem
objects) when the user requests completions (e.g. presses Ctrl+Space) (CompletionItemProvider | Monaco Editor API). EachCompletionItem
can specify a label, insert text, kind (snippet, keyword, etc.), documentation, and so on. You can also implementresolveCompletionItem
for lazy evaluation of details (CompletionItemProvider | Monaco Editor API) (CompletionItemProvider | Monaco Editor API). Completion providers can specifytriggerCharacters
to automatically activate (for example, trigger on"."
for member suggestions) (CompletionItemProvider | Monaco Editor API) (CompletionItemProvider | Monaco Editor API). The function returns anIDisposable
you can keep to later unregister the provider if needed. -
Hover Tooltips – Use
monaco.languages.registerHoverProvider(langId, provider)
to show tooltips when the user hovers over text (registerHoverProvider | Monaco Editor API). The provider implementsprovideHover(model, position, token)
and returns aHover
object containing the tooltip content and range (languages | Monaco Editor API). The content is typically given asIMarkdownString
or plain text. For example, in a programming language you might show type info or documentation in the hover. The hover appears after a short delay when the user stops moving the mouse over a symbol. -
Signature Help (Parameter Hints) – Use
monaco.languages.registerSignatureHelpProvider(langId, provider)
to show function signature and parameter info (usually triggered when the user presses "(" or on demand). The provider implementsprovideSignatureHelp(model, position, token, context)
and returns aSignatureHelp
result with one or more signatures (and highlights the active parameter) (SignatureHelp | Monaco Editor API). You can also definesignatureHelpTriggerCharacters
(e.g. "(" or ",") in the provider. This feature corresponds to VS Code’s parameter hints (usually shown in a small popup while writing function calls). -
Go to Definition & References – Use
monaco.languages.registerDefinitionProvider(langId, provider)
to enable "Go to Definition" (F12) for a language (languages | Monaco Editor API). The provider’sprovideDefinition(model, position, token)
should return aLocation
or array ofLocation
(file URI + range) for the symbol at the given position. Similarly,monaco.languages.registerReferenceProvider(langId, provider)
enables "Find All References"; itsprovideReferences(model, position, context, token)
returns an array ofLocation
where the symbol is used (languages | Monaco Editor API). Typically, these providers might query an existing language server or precomputed index to find symbols. If multiple definitions exist, Monaco will show a peek view with all locations. -
Go to Implementation / Type Definition – For OOP or typed languages, Monaco supports finding implementations and type definitions. Use
registerImplementationProvider
to provide jump-to-implementation (for an interface or abstract method) andregisterTypeDefinitionProvider
for jump-to-type (e.g. go to the type of a variable). These work like definition providers but for these specific concepts, each returningLocation
(s) for the implementation or type. -
Document Symbols (Outline) – Use
monaco.languages.registerDocumentSymbolProvider(langId, provider)
to populate the document outline (list of symbols like functions, classes, etc.). The provider’sprovideDocumentSymbols(model, token)
should return an array ofDocumentSymbol
(with name, range, kind like Function or Class) (languages | Monaco Editor API). Monaco will show these in the "Outline" tree or breadcrumb UI if integrated. -
Highlights & Occurrences – Use
monaco.languages.registerDocumentHighlightProvider(langId, provider)
to highlight all occurrences of a symbol. The provider returnsDocumentHighlight
objects for the symbol at a given position (often all instances of the word) (languages | Monaco Editor API). This is typically invoked when the user selects or places the cursor on a symbol, causing all identical references to be highlighted. -
Rename Refactoring – Use
monaco.languages.registerRenameProvider(langId, provider)
to support renaming symbols via an editor action (usually triggered by F2). The provider implementsprovideRenameEdits(model, position, newName, token)
and returns a set of text edits (aWorkspaceEdit
) to apply (RenameProvider | Monaco Editor API). Monaco will show a rename UI and apply the edits if the provider returns them. The provider can optionally implementresolveRenameLocation
to check if a position is a valid rename target and provide range of the symbol. -
Code Actions (Quick Fixes) – Use
monaco.languages.registerCodeActionProvider(langId, provider)
to offer quick fixes or refactorings (the lightbulb UI). The provider’sprovideCodeActions(model, range, context, token)
returns aCodeActionList
– basically a list ofCodeAction
with either edits or commands to run (languages | Monaco Editor API) (languages | Monaco Editor API). You can specifyCodeActionKind
for each action (e.g. quickfix, refactor). Monaco will call this when certain context conditions are met (e.g. cursor on an error or a refactor keybinding invoked) and will show a lightbulb icon (Ctrl+.
to open by default). EachCodeAction
typically includes edits to fix an issue, or a command to execute. -
Code Lens – Use
monaco.languages.registerCodeLensProvider(langId, provider)
to add CodeLens – inline, clickable annotations that appear just above source lines (e.g. “ 3 references” above a function). The provider’sprovideCodeLenses(model, token)
returns an array ofCodeLens
items, each with arange
and a command (with title and an identifier to execute) (languages | Monaco Editor API). Monaco will render the text of the command above the code at that range. If computing the commands is expensive, implementresolveCodeLens
to lazily resolve each lens’s command. This is useful for things like reference counts or run test links in editors. -
Formatting – Monaco supports formatting code via providers:
monaco.languages.registerDocumentFormattingEditProvider(langId, provider)
– for full-document formatting (invoked via Alt+Shift+F or context menu). The provider returns an array of text edits to transform the whole document (for example, pretty-print the code) (languages | Monaco Editor API).monaco.languages.registerDocumentRangeFormattingEditProvider(langId, provider)
– for formatting a selected range of code (languages | Monaco Editor API).monaco.languages.registerOnTypeFormattingEditProvider(langId, provider)
– for automatic formatting as the user types. This is triggered by specified characters (e.g.}
or;
). The provider implementsprovideOnTypeFormattingEdits(model, position, ch, options, token)
and returns edits to apply immediately when the characterch
is typed (languages | Monaco Editor API).
Providers for formatting typically hook into existing formatters or libraries for the language and simply adapt the edits.
-
Folding – By default Monaco provides indentation-based folding for languages, but you can enhance it. Use
monaco.languages.registerFoldingRangeProvider(langId, provider)
to compute custom foldable ranges (e.g. based on syntax, like #region…#endregion or AST) (languages | Monaco Editor API). The provider’sprovideFoldingRanges(model, context, token)
returns an array ofFoldingRange
(start line, end line, and an optional kind). This allows custom folding regions beyond the default. You can also configure folding markers (regex for start/end) via the language configuration. -
Inlay Hints – Use
monaco.languages.registerInlayHintsProvider(langId, provider)
to show inline hints (small grayed-out text inserted in the code, like parameter names or type hints). The provider’sprovideInlayHints(model, range, token)
returns an array ofInlayHint
with position and text for each hint (languages | Monaco Editor API) (languages | Monaco Editor API). You can specify anInlayHintKind
(e.g. Parameter or Type). Monaco will render these hints in the editor line as subtle annotations. -
Color Picker & Semantic Tokens – Additional providers exist such as
registerColorProvider
(for identifying color values in text and providing a color picker UI) (languages | Monaco Editor API), andregisterDocumentSemanticTokensProvider
(for semantic highlighting data, if you have a semantic token source). These are more advanced: a color provider returns color ranges and suggestions (IColorPresentation
) for color values; a semantic tokens provider returns a dataset of token types and modifiers for more accurate highlighting than TextMate.
Each register...Provider
call returns an IDisposable
. If you want to later remove a provider (to replace it or unload a language), call the .dispose()
on that object.
This section covers APIs for adding visual embellishments or custom UI elements inside the Monaco editor.
-
Decorations – Decorations are arbitrary styling or metadata that can be applied to ranges of text (such as colored backgrounds, squiggly underlines, glyph icons in the gutter, etc.). Use
editor.deltaDecorations(oldDecorations, newDecorations)
to add or change decorations in an editor. You provide an array ofIModelDeltaDecoration
– each with a targetrange
and anIModelDecorationOptions
describing the styling (ITextModel | Monaco Editor API) (ITextModel | Monaco Editor API). The call returns an array of decoration IDs. You should keep these IDs and pass them asoldDecorations
in the next call to update or remove those decorations (Monaco will remove any IDs you don’t include). For example, you might highlight line 10 by callingdeltaDecorations([], [{ range: new monaco.Range(10,1,10,1), options: { isWholeLine: true, className: 'myLineHighlight' } }])
. Decoration options can specify CSS class names to apply, hover messages (hoverMessage
as Markdown or string), glyph margin content (glyphMarginClassName
or an icon), inline class names, etc. This allows very flexible custom highlighting or annotations in the code. -
Decoration Collection – In newer Monaco versions, you can manage decorations more easily using decoration collections. Call
editor.createDecorationsCollection([initialDecorations])
to get anIEditorDecorationsCollection
object (IEditorDecorationsCollection | Monaco Editor API). This collection has methods like.set(newDecorations)
to apply a set of decorations and internally handles computing diffs and IDs, and.clear()
to remove all decorations (IEditorDecorationsCollection | Monaco Editor API) (IEditorDecorationsCollection | Monaco Editor API). All decorations in the collection automatically get cleaned up if the editor or model is disposed. Using a collection can be simpler than manually tracking arrays of IDs (it also ensures decorations have the correct owner and update efficiently). -
Content Widgets – A content widget is a custom DOM element rendered inline with the text, anchored to a specific position in the document (it moves as the text moves). For example, the lightbulb for code actions or an inline error peek could be a content widget. To add one, implement the
IContentWidget
interface for your widget: give it angetId()
, agetDomNode()
returning an HTMLElement, and agetPosition()
that specifies itsrange
or position and preference for placement (IContentWidget | Monaco Editor API) (IContentWidget | Monaco Editor API). Then calleditor.addContentWidget(widget)
. The editor will position your element in the editor layer. You can update its position by returning a new position ingetPosition()
when needed and callingeditor.layoutContentWidget(widget)
. Content widgets can allow overflow (e.g., to not clip outside editor) via theallowEditorOverflow
property (IContentWidget | Monaco Editor API). A common use is rendering error messages or interactive controls at specific text locations. -
Overlay Widgets – An overlay widget is a DOM element rendered on top of the editor, not tied to the content flow. It is positioned in an absolute position relative to the editor (such as top right corner or bottom). Examples are the find/replace bar or a watermark. To add one, implement
IOverlayWidget
withgetId()
,getDomNode()
andgetPosition()
(IOverlayWidget | Monaco Editor API). The position is specified by anIOverlayWidgetPosition
(which can havepreference
like top-right, etc.). Calleditor.addOverlayWidget(widget)
to add it. Overlay widgets are always rendered above the text (in a separate DOM layer) (IOverlayWidget | Monaco Editor API). Useeditor.layoutOverlayWidget(widget)
if you need to reposition it on the fly. If an overlay might overflow the editor (e.g. a suggest widget), setallowEditorOverflow
to true (IOverlayWidget | Monaco Editor API). -
View Zones – A view zone is a full-width, block element that pushes lines of text apart. It’s used to insert UI between lines of code. For example, the inline diff review or a live preview below a line uses a view zone. To add a view zone, call
editor.changeViewZones(callback)
. In the callback, use the providedIViewZoneChangeAccessor
toaddZone(zone)
wherezone
is anIViewZone
object (IViewZoneChangeAccessor | Monaco Editor API). Key properties ofIViewZone
are:afterLineNumber
(to place it after a certain line, or 0 to put at top),heightInLines
orheightInPx
(how tall the zone is), and thedomNode
which is the DOM element to insert (IViewZone | Monaco Editor API) (IViewZone | Monaco Editor API). The editor will allocate space for your domNode of that height, shifting text below it. The accessor also lets youremoveZone(id)
by the ID returned from addZone (IViewZoneChangeAccessor | Monaco Editor API), orlayoutZone(id)
to recompute its positioning (IViewZoneChangeAccessor | Monaco Editor API). View zones are great for things like showing function documentation between lines, reserving space for widgets, or implementing features like inline suggestion previews. -
CodeLens – CodeLens are those little clickable annotations that appear above a line (commonly “n references” or “Run test”). They are implemented via the CodeLens provider as described in Language Features, but it’s worth noting here since they are visual. If a CodeLens provider is registered, Monaco will render the lenses in the editor. The visual style of CodeLens can be customized via CSS (they typically appear as small, gray text). Each CodeLens item comes from the provider with a command; clicking the lens triggers that command. There isn’t a direct editor method to add CodeLens; you must use
registerCodeLensProvider
. -
Inlay Hints – Similarly, Inlay hints (small text inserted in line, like parameter names) are provided via an InlayHints provider. Monaco renders them as part of the text with a distinct style. You don’t add them via editor API directly, but we include them here as they are visual enhancements controlled by the provider (see Language Features above).
-
Custom Themes for Decorations – If your decorations use themable colors (via
ThemeColor
in the decoration options), you might define those in your custom theme (see Themes and Appearance below). For example, you could use a theme key like"editorError.foreground"
for an error underline, so that it adapts to dark/light themes automatically. The Monaco theming system will apply those if properly set.
Monaco separates the notion of markers (diagnostic information like errors or warnings) from the core text model. Markers are typically produced by a language service or your own validation logic, and you apply them to models via the marker API. Markers cause red/yellow squiggly underlines in the editor, show up in the gutter with icons, and can be listed in an Problems pane if you have one.
-
monaco.editor.setModelMarkers(model, owner, markers)
– Sets the markers for a given text model (setModelMarkers | Monaco Editor API). You provide an array ofIMarkerData
objects describing each marker (usually errors, warnings, etc.). EachIMarkerData
includes at least: a range (startLineNumber
,startColumn
,endLineNumber
,endColumn
), amessage
(string description), aseverity
(see MarkerSeverity), and anowner
string. Theowner
is an identifier for the source of markers (e.g. a language or a plugin name) – it allows multiple independent sets of markers on the same model. Calling setModelMarkers replaces all markers for that model from the given owner. For example, you might use owner ="eslint"
for markers coming from an ESLint check, and"compiler"
for markers from a compiler. -
Marker Severity and Tags – The
monaco.MarkerSeverity
enumeration defines the levels:Hint
(lowest),Info
,Warning
, andError
(highest) (IMarker | Monaco Editor API). Markers with higher severity may be rendered differently (e.g. error = red squiggly, warning = yellow squiggly, info = blue squiggly). Monaco also definesmonaco.MarkerTag
for optional metadata tags on markers (IMarker | Monaco Editor API). For example,Unnecessary
tag can be used to deemphasize code (often rendered as faded text), andDeprecated
tag might add a strikethrough. You can specifytags: [monaco.MarkerTag.Unnecessary]
inIMarkerData
to indicate a diagnostic that is not critical (like unused variable). These tags influence editor rendering if the theme supports it. -
Retrieving Markers – Use
monaco.editor.getModelMarkers(filter)
to get markers. You can filter by owner, resource (URI), or severity (editor | Monaco Editor API). For example,getModelMarkers({ owner: 'eslint' })
gives all markers from ESLint on all models, orgetModelMarkers({ resource: model.uri })
gives all markers on a specific model. The returned objects are of typeIMarker
which includes the marker data plus theresource
(model URI) andowner
(IMarker | Monaco Editor API) (IMarker | Monaco Editor API). This is useful to display a consolidated error list. -
Clearing Markers – To clear markers, call
monaco.editor.setModelMarkers(model, owner, [])
with an empty list for that owner, or usemonaco.editor.removeAllMarkers(owner)
to clear all markers for a given owner across all models (editor | Monaco Editor API). If a model is disposed, its markers are automatically cleared. -
Listening to Marker Changes – Monaco provides a global event
monaco.editor.onDidChangeMarkers
which fires whenever markers change on any model (editor | Monaco Editor API). The listener receives an array of model URIs that had marker updates. You can use this to update UI elements like an error count badge. -
Using Markers for Diagnostics – Markers are the primary way to show errors from language validation or linters. For example, a JSON language service might produce a list of errors with line/col – you convert those to
IMarkerData
and set them on the model. The editor will then draw red underlines for errors and show the messages on hover. Markers can also be used for warnings, infos, or hints (e.g. hint could be used for stylistic suggestions). The Problems panel in an IDE context can be populated by collecting markers viagetModelMarkers
. Markers do not directly remove themselves; you must update or clear them as conditions change (e.g. on model content change, you re-run validation and then update markers). -
Note on Decoration vs Marker – If you need to just highlight text without an error/warning semantics (no message or icon), you might use a decoration instead. Markers are specifically for diagnostics and carry severity and messages which show up on hover. They are also used by Monaco’s standalone glyph margin where an error marker will show a red squiggly line and red icon, etc. Under the hood, Monaco actually turns markers into decorations with specific styles (so you don’t manually create error decorations; you create markers and let Monaco render them according to severity).
Monaco Editor allows you to add custom commands and keyboard shortcuts, and to invoke built-in commands. There are two levels to consider: editor instance commands (bound to a specific editor), and global keybindings (managed by Monaco’s keybinding service).
-
Editor Commands – You can add a command that is scoped to a particular editor instance using
editor.addCommand(keybinding, handler, [context])
. This returns a command ID (string) (IStandaloneCodeEditor | Monaco Editor API). Thekeybinding
is a key code defined usingmonaco.KeyCode
andmonaco.KeyMod
(see below), andhandler
is a function to execute. Optionally,context
is a condition string that must be truthy in the editor’s context (you can use Monaco’s context key service, though for simple uses this can be omitted or a context liketextFocus
). Example:editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, () => alert('Ctrl+K pressed'));
. This command would only work when that editor has focus. -
Editor Actions – An editor action is a higher-level construct that can include a menu item, label, and optional keybinding. You can define one via
editor.addAction(descriptor)
. The descriptor is anIActionDescriptor
with properties:id
(unique),label
(for UI like context menu),keybindings
(array of key codes like above),precondition
(context when it’s enabled), and therun(editor)
function to execute (IStandaloneCodeEditor | Monaco Editor API). Actions appear in the editor’s context menu and can also be invoked via the command palette if your integration has one. Use actions for features that you want accessible via right-click or palette. For example, you might add an action "Sort Lines" that when run, sorts the selected lines. -
Global Keybindings – Monaco’s KeybindingRegistry can be accessed via static functions. For instance,
monaco.editor.addKeybindingRule(rule)
adds a keybinding globally. A rule is anIKeybindingRule
object with akeybinding
(integer, combine KeyMod/KeyCode), acommand
ID, and optionalwhen
context expression. You can also add an array of rules withaddKeybindingRules([...])
. These are global in the sense that they apply to any editor that is focused, unless an editor-specific override exists. The command ID can be one of Monaco’s built-in commands or a custom one you’ve registered. -
Registering Commands – If you want to create a command that isn’t tied to one editor instance, use
monaco.editor.registerCommand(id, handler)
. This registers a command ID with a handler function globally (the handler gets anaccessor
to services and any arguments) (registerCommand | Monaco Editor API). Once registered, you can use that command ID in keybinding rules or trigger it from any editor viaeditor.trigger(...)
. This is analogous to VS Code’s command registry. The function returns anIDisposable
to unregister. Note: for most web embedding scenarios, you might not need global commands unless you have multiple editors and want a common command across them. -
Built-in Commands – Monaco comes with many built-in commands (most are identical to VS Code’s). Examples of command IDs:
'undo'
,'redo'
,'editor.action.commentLine'
(toggles line comment),'editor.action.formatDocument'
,'editor.action.addCursorAbove'
(multi-cursor), etc. You can invoke any of these witheditor.trigger('keyboard', commandId, null)
. You can also assign new keybindings to them using the methods above. For instance, to bind Ctrl+K to format, you could doaddKeybindingRule({ keybinding: monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, command: 'editor.action.formatDocument' })
. (By default Monaco has standard keybindings for these, but you can change or add your own.) -
Key Codes and Modifiers – Monaco provides the
monaco.KeyCode
enum for all keys (letters, F1-F12, etc.) (KeyCode | Monaco Editor API) and themonaco.KeyMod
bit masks for modifier keys (KeyMod | Monaco Editor API) (KeyMod | Monaco Editor API).KeyMod.CtrlCmd
represents "Ctrl" on Windows/Linux and "⌘ Cmd" on Mac.KeyMod.Shift
andKeyMod.Alt
represent Shift and Alt, andKeyMod.WinCtrl
is the Windows key (or Ctrl on Mac, typically not used often). You combine them by bitwise OR. For example:KeyMod.CtrlCmd | KeyCode.KEY_S
for Ctrl+S / Cmd+S.KeyMod.Alt | KeyCode.F2
for Alt+F2. To represent chords (two-step sequences like VS Code’s Ctrl+K then Ctrl+C), usemonaco.KeyMod.chord(first, second)
(KeyMod | Monaco Editor API), though chorded keybindings are less common in web contexts.
-
Context Keys – Monaco has a context key service that allows enabling/disabling commands or keybindings based on context (like whether editor has focus, or if text is selected, etc.). Common context keys include
editorTextFocus
(true when an editor has text focus),editorHasSelection
,editorLangId == <language>
, etc. When adding keybinding rules or actions, you can specifywhen: "editorTextFocus && !editorHasSelection"
for example to make something only work when there is no selection. Though deep use of context keys might not be needed for basic integrations, it’s how Monaco mirrors VS Code’s conditional keybindings. -
Removing or Changing Keybindings – Monaco doesn’t provide a direct remove function for specific default keybindings in the API, but you can override them by registering a different command to the same key or by using the
addKeybindingRules
with a specific weight (Monaco’s keybinding system uses priorities). In an embed, if needed, you can also intercept keyboard events before they reach Monaco if you want to implement your own handling, but using Monaco’s APIs is preferred to maintain accessibility and consistency. -
Command Palette – Monaco doesn’t include a GUI command palette by default (that’s more of an application feature in VS Code). However, you could create one by listing all actions (use
editor.getSupportedActions()
to get actions added to an editor) (IStandaloneDiffEditor | Monaco Editor API) (IStandaloneDiffEditor | Monaco Editor API) and invoking theirrun
methods or triggering their IDs. This is beyond the core API but good to know the pieces exist.
Monaco Editor is themable and highly configurable in appearance. By default it comes with a few built-in themes and many options to tweak the look and feel.
-
Built-in Themes – Monaco includes three built-in themes:
"vs"
(Visual Studio light),"vs-dark"
(dark theme), and"hc-black"
(high-contrast black theme). You can switch theme at any time usingmonaco.editor.setTheme(themeName)
. For example,monaco.editor.setTheme("vs-dark")
will apply the dark theme (setTheme | Monaco Editor API). If you don’t set a theme, the default is usually "vs" (light). -
Custom Themes – You can define your own themes to match your app. Use
monaco.editor.defineTheme(themeName, themeData)
to create a theme or override an existing one (defineTheme | Monaco Editor API). ThethemeData
is anIStandaloneThemeData
object where you specify:- base: the base theme to inherit from (
"vs"
,"vs-dark"
, or"hc-black"
are the base options) (IStandaloneThemeData | Monaco Editor API). - inherit: boolean, set to
true
if you want to inherit default styling from the base (usually yes) (IStandaloneThemeData | Monaco Editor API). - rules: an array of
ITokenThemeRule
for syntax tokens (IStandaloneThemeData | Monaco Editor API). Each rule can target a token scope (like "comment" or "keyword") and set a foreground color and font style. This is similar to VS Code’stokenColors
in themes. - colors: an object of UI theming colors (IStandaloneThemeData | Monaco Editor API). These use editor color keys (like
"editor.background"
,"editorLineNumber.foreground"
,"editorCursor.foreground"
, etc.) as properties and color hex codes as values. Through this you can recolor the editor’s UI elements.
Once defined, you can activate the theme with
setTheme(name)
. Custom themes allow you to ensure Monaco matches your application’s color scheme. - base: the base theme to inherit from (
-
Editor Options for Appearance – A large number of editor configuration options control appearance. These can be set during creation (via the options object) or changed later with
editor.updateOptions(newOptions)
. Some commonly used appearance-related options:fontSize
,fontFamily
,fontLigatures
– control the text rendering font and size.lineNumbers
:"on"
,"off"
,"relative"
, or a function – controls the display of line numbers."relative"
will number lines relative to the current cursor position (like in Vim) (Relative line numbers for monaco editor - Stack Overflow).lineDecorationsWidth
andglyphMargin
: to adjust gutter width or toggle the glyph margin (space for breakpoints, icons).minimap
: an object{ enabled: bool, side: 'right'|'left', scale: number, showSlider: 'always'|'mouseover' }
– controls the code minimap on the side.wordWrap
: can be"off"
,"on"
,"wordWrapColumn"
, or"bounded"
– determines how/whether lines wrap. If wrapping, you might also setwordWrapColumn
.lineHeight
: can tweak line spacing.renderWhitespace
:"none"
,"boundary"
, or"all"
– to show whitespace characters.renderIndentGuides
: boolean – whether to show vertical guide lines for indentation levels.roundedSelection
: boolean – whether the selection uses rounded borders.theme
: although the theme is usually set viasetTheme
, you can also specify a theme in the options on creation (it will call setTheme internally).
Most of these options are listed in the
IEditorOptions
interface (IEditorOptions | Monaco Editor API) (IEditorOptions | Monaco Editor API). They enable you to finely tune how the editor looks (for instance, an IDE might turn on minimap and indent guides, whereas a simple embed might turn off line numbers and minimap for a cleaner look). -
High Contrast and Accessibility – Monaco has an accessibility support setting (
accessibilitySupport: "auto" | "on" | "off"
in options) (IEditorOptions | Monaco Editor API). When set to "on", it optimizes for screen readers (for example, it might disable certain optimizations that confuse screen readers). In high contrast themes (hc-black
or a custom HC theme), ensure your color choices meet contrast requirements. Monaco’s default HC theme uses very distinct colors to maximize readability. -
Editor Layout & Styling – The editor will automatically use the size of its container. To ensure it’s styled correctly, you may want to set an explicit height/width on the container element or use Flexbox/absolute positioning. The editor’s theme and font are independent of the rest of the page – it creates its own internal styles. If you need to override something via CSS, you can target the container’s class (Monaco adds classes like
.vs
or.vs-dark
to the root based on theme). However, most customizations have an API option. -
Customizing Specific Elements – Some fine-grained visuals can be customized via CSS or theme colors. For example, the color of the caret (cursor) is set by the theme color
editorCursor.foreground
. Selection color iseditor.selectionBackground
. If you want to style find highlight or bracket match highlight, there are theme keys for those as well (editor.findMatchBackground
,editorBracketMatch.background
, etc.). In custom themes, set these in thecolors
object. If something isn’t themeable, you may resort to injecting CSS – but Monaco covers the majority of editor UI in theming. -
Remeasuring Fonts – If you dynamically load a font for the editor (say a custom monospace font) and apply it, you might need to nudge Monaco to recalc character widths. You can call
monaco.editor.remeasureFonts()
to force it to measure the font metrics again (Monaco Editor v0.34.0 release notes | LibHunt). Typically, Monaco periodically checks, but this ensures immediate update if needed.
By combining theme definitions, editor options, and the marker/decorations API, you have full control over the Monaco Editor’s appearance to integrate it seamlessly into your application’s design.
Monaco’s API includes a number of utility classes, types, and enums that are used across the editor and language APIs. These are generally found on the global monaco
namespace.
-
monaco.Uri
– A URI (Uniform Resource Identifier) class used to identify models and other resources. Monaco models are indexed byUri
(seemodel.uri
). You can create a Uri via static methods:monaco.Uri.parse("scheme://authority/path")
ormonaco.Uri.file("/absolute/path")
. In browser usage, models often use a scheme like"inmemory"
or"file"
with an arbitrary path. For example:Uri.parse("inmemory://model/1")
. TheUri
object has properties likescheme
,path
,fragment
, etc., and a.toString()
. Using URIs allows Monaco to treat models similar to files, and link things like definitions across models (e.g. go to definition may return a Uri of another model to open). -
Positions and Ranges – Monaco uses 1-indexed positions (line numbers and columns start at 1, like in most text editors). The
Position
class represents a point in the model (line & column). You can construct one withnew monaco.Position(lineNumber, column)
or use static helperPosition.lift(IPosition)
.Position
has methods likewith(newLine, newCol)
to create a adjusted copy, and you can compare positions (e.g.position.isBefore(other)
). TheRange
class represents a text span between a start position and end position (inclusive of start, exclusive of end). You can create vianew monaco.Range(startLine, startCol, endLine, endCol)
. Range has utility methods: e.g.Range.containsPosition(pos)
to check if a position is inside,Range.intersectRange(otherRange)
to get overlap, and static helpers likeRange.fromPositions(start, end)
. In many API calls, you can use the interface shape{ startLineNumber, startColumn, endLineNumber, endColumn }
interchangeably with a Range instance – Monaco will accept either. For multi-line selections or decorations, ranges are fundamental. -
Selection
– This class extends Range, addingselectionStartLineNumber
,selectionStartColumn
(the anchor point where the user started the selection) in addition topositionLineNumber
andpositionColumn
(the active end of the selection). It also has agetDirection()
to tell if the selection is LTR or RTL (from top-left to bottom-right vs reverse) (Monaco Editor API). Usually you won’t need the direction, but Monaco uses it to maintain the cursor position when extending a selection. You can create a Selection withnew monaco.Selection(selectionStartLine, selectionStartCol, positionLine, positionCol)
. The editor’sgetSelection()
returns a Selection, andgetSelections()
returns an array of them (for each cursor). You can also use staticSelection.fromPositions(start, end)
if needed. -
IDisposable
– Many Monaco APIs return anIDisposable
. This is a simple interface with a single methoddispose()
. It’s Monaco’s way of letting you unregister or cleanup something. For example, registering an event listener returns IDisposable – call.dispose()
on it to remove the listener (registerCommand | Monaco Editor API). Similarly, language providers, commands, editor actions all return disposables. Always keep track of disposables for things you register, so you can dispose them when unloading a editor or changing a model, to prevent memory leaks. Monaco’s core will also dispose certain things automatically (e.g. when an editor is disposed it disposes all its attached listeners and contributions), but it’s good practice to manage your own. -
IEvent
– Monaco’s event mechanism uses theIEvent
interface for type safety. You typically don’t create these yourself; instead, you subscribe to events. For example, an event propertyonDidChangeContent: IEvent<IModelContentChangedEvent>
can be used by callingmodel.onDidChangeContent(listener)
. In practice, calling the property as a function with a listener subscribes it and returns IDisposable. Just be aware that when you see something typed as IEvent, you use it by passing a callback. -
monaco.KeyCode
– An enum of keyboard keys, used for identifying keys in keybindings (KeyCode | Monaco Editor API). Examples:KeyCode.Enter
,KeyCode.F1
,KeyCode.KeyA
(for letter A),KeyCode.Digit0
(for 0 key),KeyCode.Escape
, etc. This is used in combination withKeyMod
for shortcuts, but you can also see these codes in some low-level key events if you work with the editor’s onKeyDown/onKeyUp. -
monaco.KeyMod
– A utility for keyboard modifier keys. It has static properties:.CtrlCmd
,.Shift
,.Alt
,.WinCtrl
which represent modifier bits (KeyMod | Monaco Editor API). As discussed in Commands section, combine them with key codes to define shortcuts. It also has a helperKeyMod.chord()
to create chorded shortcuts (KeyMod | Monaco Editor API). If you inspect a keybinding number (the integer), it’s a combination of these bits. -
Cancellation Tokens – Monaco defines
CancellationToken
andCancellationTokenSource
to signal cancellation of async operations. Many provider methods receive atoken: CancellationToken
parameter (CompletionItemProvider | Monaco Editor API). Iftoken.isCancellationRequested
becomes true (for instance, user moved on and the operation is no longer needed), your provider should abort its work. You can create aCancellationTokenSource()
and passsource.token
to some API, and later callsource.cancel()
to cancel. In most cases you won’t create tokens yourself; Monaco does (for example, it will cancel an outdated hover request if the user moves the mouse). -
Promises/Thenable – Monaco uses native Promises. It also has a type alias
Thenable<T>
which is basically “Promise-like object”. This is for compatibility with VS Code’s API which uses Thenable in its typings. You can freely use realPromise<T>
in place of Thenable. For providers, you can often return either a value or a Promise (that’s why many return types areProviderResult<T>
which allows either sync or async). -
monaco.Emitter
– A class to create your own event. If you build a Monaco extension or integrate in a deeper way, you might usenew monaco.Emitter()
to create an event-emitting object. It has a.event
property (the IEvent to subscribe to) and methods tofire
events. Average Monaco usage doesn’t require this, but it’s there if you need custom events. -
Miscellaneous Utilities:
monaco.editor.colorize
– a function to syntax-highlight a string of code in a given language, returning HTML (useful to display colored code outside of an editor) (colorize | Monaco Editor API).monaco.editor.colorizeElement
– highlight the code within a DOM element in-place (for static code snippets).monaco.editor.tokenize
– low-level function that returns tokens for a given text and language without needing an editor (editor | Monaco Editor API).monaco.editor.MonacoWebWorker
– class used when you create a web worker viacreateWebWorker
. It provides agetProxy()
to call methods on the worker and awithSyncedResources
to ensure certain model URIs are synced to the worker. This is advanced, but if you implement language logic in a web worker (for performance), Monaco helps with syncing model content to that worker (createWebWorker | Monaco Editor API).- Enums: Other enums include
monaco.editor.ScrollType
(smooth vs immediate scrolling),monaco.editor.CursorChangeReason
,monaco.editor.EditorOption
(an enum of all editor option keys), etc. These are typically used internally or for certain APIs (for example,revealPosition(pos, ScrollType.Smooth)
).
All these types come together to support Monaco’s operation. As a developer, you’ll frequently use Uri
, Position
, Range
, and Selection
when working with the editor programmatically, and use enums like KeyCode
/KeyMod
and MarkerSeverity
for specific functionalities. Understanding these building blocks will make the Monaco Editor API much easier to navigate and utilize effectively (Monaco Editor API) (Monaco Editor API).