BlazorMonaco: a Monaco Editor component for Blazor
Blazor component for Microsoft's Monaco Editor which powers Visual Studio Code.
At a glance
- What is it?
- BlazorMonaco wraps Microsoft's Monaco Editor, the engine behind Visual Studio Code, in a Blazor component so a Razor page can host a real code editor with a JavaScript interop layer. It ships standalone and diff editors, event subscriptions, CSS hooks and static wrappers for the global JavaScript methods, and it tracks a specific Monaco version.
- Who is it for?
- Adopt BlazorMonaco if you are building a Blazor application that needs a real code editor rather than a plain textarea, and you are willing to load the Monaco assets as static web assets and wire the script tags into your host page. Do not adopt it if you need the whole Monaco feature set, because the README states that less frequently used features are currently missing, or if you cannot add the three script tags, since the editor will not initialise without them.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 117 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What BlazorMonaco is, and what Monaco Editor gives you
BlazorMonaco is a Blazor component for the Microsoft Monaco Editor, which is the editor engine that powers Visual Studio Code. It is published on NuGet as BlazorMonaco, MIT licensed, and the current version uses Monaco Editor v0.55.1 and supports netstandard2.0 plus net5.0 through net10.0.
The framing of coverage is worth quoting honestly, because the README is candid about it. Most of the main Monaco Editor feature set is supported, with some less-frequently used features currently missing. Contributions, comments and suggestions are welcomed, and the author invites contact through GitHub.
That sentence is the scope statement for the whole project. This is a binding layer, not a reimplementation. The editor itself is Monaco, running in the browser as JavaScript, and BlazorMonaco's job is to let a Razor component talk to it.
Which raises the obvious design question, and the README answers it in the installation steps. The Monaco assets are served from the package as static web assets under the _content/BlazorMonaco path, and you have to add three script tags to the end of your html body tag, and they must be placed before the Blazor script tag, whether that is blazor.webassembly.js, blazor.server.js or blazor.web.js. The first is a jsInterop.js file that comes from the package, and the other two are the Monaco loader and the editor bundle.
So the deployment model is: a NuGet package, a Blazor component, and a JavaScript library loaded from the same package. There is a working WebAssembly sample app linked from the README if you want to see it running before you commit to it.
The three steps to get an editor on a page
Installation is documented as a sequence, and it is short enough to follow end to end.
First, add the NuGet package to the Blazor project:
dotnet add package BlazorMonaco
// or
Install-Package BlazorMonacoSecond, add the script tags to the end of the html body tag, before the Blazor script tag:
<script src="_content/BlazorMonaco/jsInterop.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>The order matters, and the README states the placement requirement explicitly. The jsInterop file is what C# calls into, and the Monaco loader and editor bundle are the editor itself.
Third, add the using directives. Everything lives in three namespaces, and the README suggests putting them in the root _Imports.razor file:
@using BlazorMonaco
@using BlazorMonaco.Editor
@using BlazorMonaco.LanguagesAnd then the component itself goes in the razor file, either a StandaloneCodeEditor or a StandaloneDiffEditor:
<StandaloneCodeEditor />
<!-- or -->
<StandaloneDiffEditor />With the default options, the editor renders. There is a troubleshooting section in the README for the cases where the editor is not visible or does not initialise correctly, and the note right after the install steps points you there.
The three script tags are the part most likely to be forgotten, especially in a Blazor Web App where the layout differs between the server and WebAssembly hosting models, and a missing or misordered script tag produces a blank div rather than an error message.
Construction options, events, and the diff editor's two inner editors
The component gives you a callback for initial options rather than a parameter object, which is a Blazor-idiomatic way to handle a large options object. You pass a method name to ConstructionOptions and the method returns a StandaloneEditorConstructionOptions:
<StandaloneCodeEditor Id="my-code-editor" ConstructionOptions="EditorConstructionOptions" />
@code {
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
{
return new StandaloneEditorConstructionOptions
{
AutomaticLayout = true,
Language = "javascript",
Value = "function xyz() {\n" +
" console.log(\"Hello world!\");\n" +
"}"
};
}
}The method receives the editor instance as a parameter, which means the options can be computed from the component itself rather than being a static block.
Events are exposed as component parameters, and the naming maps to the Monaco event names. The README mentions OnDidKeyUp and OnDidPaste as examples:
<StandaloneCodeEditor Id="my-code-editor" OnDidChangeCursorPosition="EditorDidChangeCursorPosition" />
@code {
private void EditorDidChangeCursorPosition(CursorPositionChangedEvent eventArgs)
{
Console.WriteLine("EditorDidChangeCursorPosition");
}
}The diff editor is where the API is slightly different, and this is the part worth understanding before you build on it. StandaloneDiffEditor exposes two properties, OriginalEditor and ModifiedEditor, for reaching the inner editor instances, and you use them like any other StandaloneCodeEditor. You can also register to the inner editors' events using the helper event parameters of the main component, with OnKeyUpOriginal and OnKeyUpModified as the examples:
<StandaloneDiffEditor @ref="_diffEditor" Id="my-diff-editor" OnKeyUpOriginal="OnKeyUpOriginal" OnKeyUpModified="OnKeyUpModified" />
@code {
private StandaloneDiffEditor _diffEditor;
private void OnKeyUpOriginal(KeyboardEvent keyboardEvent)
{
StandaloneCodeEditor originalEditor = _diffEditor.OriginalEditor;
Console.WriteLine("OnKeyUpOriginal : " + keyboardEvent.Code);
}
private void OnKeyUpModified(KeyboardEvent keyboardEvent)
{
StandaloneCodeEditor modifiedEditor = _diffEditor.ModifiedEditor;
Console.WriteLine("OnKeyUpModified : " + keyboardEvent.Code);
}
}Note the @ref on the diff editor. You need a reference to the component in order to get at OriginalEditor and ModifiedEditor from inside the handlers, and the reference is the only route to the inner instances.
Styling from CSS, and the three selectors you get
The styling story is a short, specific contract, and it is one of the nicer parts of the design because it does not fight the CSS model.
There are three css selectors you can use. All editor instances are contained in a div element that has a css class of monaco-editor-container. The Id value you set for your editor instance is also set as the id of its container div element. And if you set the editor's CssClass property, that value is added to the class attribute of its container div element.
The razor component:
<StandaloneCodeEditor Id="my-editor-id" CssClass="my-editor-class" />is rendered with the html below:
<div id="my-editor-id" class="monaco-editor-container my-editor-class">
...
</div>So the three selectors map to three different scopes. The id selector targets one specific editor instance, the class you set targets every editor instance carrying that class, and monaco-editor-container targets every editor instance on the page:
#my-editor-id { /* applies to a specific editor instance */
height: 100px;
}
.my-editor-class { /* applies to all editor instances with this class */
height: 100px;
}
.monaco-editor-container { /* applies to all editor instances */
height: 100px;
}The practical consequence is that the height of an editor is not set through the editor options, it is set through CSS on the container. That is a deliberate choice, and it is what makes it possible to have a set of editors whose heights are driven by a layout system rather than by hard-coded pixel values in each component.
Global methods, custom Monaco builds, and where the project stands
Two features handle the awkward cases of embedding a JavaScript library in a C# world.
The first is the global method problem. Monaco Editor's JavaScript library defines some methods in the global scope, and since C# does not allow global methods, BlazorMonaco groups those methods in two static classes named BlazorMonaco.Editor.Global and BlazorMonaco.Language.Global. If you need a Monaco method that lives in the global scope, you look up where it is in the JavaScript library and find it in the corresponding Global class:
await BlazorMonaco.Editor.Global.SetTheme(jsRuntime, "my-custom-theme");The second is custom Monaco installations. If you have made changes to the Monaco Editor JavaScript library and need to use your edited version rather than the unmodified one embedded in BlazorMonaco, the README shows modifying the script tags accordingly, replacing the bundled editor paths with your own require configuration.
That escape hatch is worth knowing about, because it is the answer to a common problem. The package embeds a specific Monaco version, 0.55.1 at the time of writing, and if you need a newer one or a patched build, you are expected to serve it yourself rather than wait for a package release.
The project structure gives a sense of its shape. There is a BlazorMonaco.sln, a BlazorMonaco directory for the component, a SampleWasmApp and a SampleWebApp, a docs directory, and a CHANGELOG.md and MIGRATE.md. The migration file is a good sign, since interop-based components do break between versions when the underlying JavaScript API changes.
The releases are regular: v3.5.0 on 2026-06-28, v3.4.0 on 2025-10-26 and v3.3.0 on 2024-12-26, and the last push to the repository was on 2026-06-28. The homepage is a GitHub Pages site hosting the documentation, and the repository is not archived.
Editorial conclusion
Adopt BlazorMonaco if you are building a Blazor application that needs a real code editor rather than a plain textarea, and you are willing to load the Monaco assets as static web assets and wire the script tags into your host page. Do not adopt it if you need the whole Monaco feature set, because the README states that less frequently used features are currently missing, or if you cannot add the three script tags, since the editor will not initialise without them. Before you upgrade, check the CHANGELOG.md and MIGRATE.md files, since the component sits on JavaScript interop with a Monaco Editor version embedded in the package, and the bundled version is 0.55.1.
Frequently asked questions
What is BlazorMonaco and which Monaco Editor version does it use?
It is a Blazor component for the Microsoft Monaco Editor that powers Visual Studio Code. The current version uses Monaco Editor v0.55.1 and supports netstandard2.0 plus net5.0 through net10.0, and it is published on NuGet as BlazorMonaco under the MIT licence.
What do I need to add to my Blazor page to get the editor working?
Add the NuGet package, then add three script tags to the end of the html body tag, before the Blazor script tag: the jsInterop.js file from _content/BlazorMonaco, and the Monaco loader.js and editor.main.js files under _content/BlazorMonaco/lib/monaco-editor/min/vs. Then add the BlazorMonaco, BlazorMonaco.Editor and BlazorMonaco.Languages using directives.
How do I set the initial options for a BlazorMonaco editor?
Set the ConstructionOptions parameter to the name of a method that returns a StandaloneEditorConstructionOptions. The method receives the editor instance as a parameter, so the options can be computed rather than hard coded.
How do I style BlazorMonaco editors with CSS?
Three selectors are available. Every editor sits in a div with the class monaco-editor-container, the Id you set becomes the id of that container div, and the CssClass property is added to its class attribute, so you can target one instance by id, a group by your own class, or all of them with monaco-editor-container.
How do I call a Monaco Editor global method from C#?
Global methods are grouped into two static classes, BlazorMonaco.Editor.Global and BlazorMonaco.Language.Global, because C# does not allow global methods. You look up where the method lives in the JavaScript library and call it from the corresponding class, for example await BlazorMonaco.Editor.Global.SetTheme(jsRuntime, "my-custom-theme").
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/serdarciplak-blazormonaco)Was this useful?