Sep 25th 2025

A toolbox for editor-centric web applications

Kai SalmenKai Salmen

Eight years after the monaco-languageclient project started, we are happy to announce we have released version 10 of the monaco-languageclient package.

The new version contains all tools needed to create complete editor-centric web applications, and thus it goes beyond just being a library for language server protocol (LSP) integration with the monaco-editor.

Skip the retrospective and directly jump to chapter “Today: Version 10” if you are already familiar with monaco-languageclient and just want to know what’s new.

How did we get here? A collaborative success story

We have not released a blog-post about monaco-languageclient in a long time. Therefore I want to take the opportunity to start with a retrospective summary. In the following sub-chapters, past important events are linearly listed. I will explain the actual purpose of the monaco-languageclient package and how it evolved and changed over time.

2017 - 2021: Project start and cool-down phase

In 2017, TypeFox launched the monaco-languageclient project as a library designed to integrate language clients with monaco-editor. This is the prerequisite to use the language server protocol to connect monaco-editor to language servers. Using LSP was only possible with VS Code and not with the self-standing monaco-editor, i.e. LSP support was missing when deploying the monaco-editor e.g. in browser applications.

From the beginning the monaco-languageclient was a wrapper around the vscode-languageclient library that was written to be used in VS Code. It expects to be run in a VS Code environment where the complete VS Code API implementation is available. This is not the case when you use monaco-editor in the browser. Until today, VS Code is provided as one monolithic piece of software. It is unavailable as one or multiple node packages distributed via a package registry like npmjs. The only exception is the artificially extracted monaco-editor.

monaco-editor npm is artificially extracted from VS Code

Thus it became required to re-implement larger parts of the VS Code API. Additionally, a LSP to monaco-editor API bridge was needed to get language server protocol messages in and out of monaco-editor.

Bi-directional API bridge from LSP to monaco-editor

In parallel to monaco-languageclient TypeFox initiated the vscode-ws-jsonrpc library. It made it possible to connect a language client running in the browser to a language server running elsewhere via web sockets. Both packages were required to integrate existing language servers into monaco-editor.

TypeFox maintained the project for about three years, but our focus moved away as Theia IDE discontinued to rely on monaco-languageclient. At this time, Loïc Mangeonjean from CodinGame stepped in as a maintainer and he did this mostly alone until 2022.

2022: Ignition

In early 2022 TypeFox saw the opportunity to build a reusable web component around monaco-editor utilizing monaco-languageclient to get language server support in web apps. At this point I joined Loïc as a maintainer of the monaco-languageclient project.

As VS Code evolved, monaco-languageclient fought compatibility problems due to its own VS Code API re-implementation. Loïc came up with the idea to transform the VS Code source code into ESM modules, so the need to partially re-implement the VS Code API effectively disappeared.

In June 2022 he released the first version of monaco-vscode-api which can be perceived as a modularized version of VS Code Web. Since then, the conglomerate of packages evolved independently from monaco-languageclient.

monaco-vscode-api supplies the VS Code API plus many additional services distributed via many different packages (A complete list of services is listed in the project’s wiki). By relying on this package conglomerate it became possible to build applications with features being available in VS Code only until then (see the official demo and CodeSandbox that relies on it).

vscode-ws-jsonrpc was integrated into the monaco-languageclient repository to streamline maintenance efforts. The initial web-component eventually evolved into the monaco-editor-wrapper package, making it easier to integrate monaco-editor with language clients into other JavaScript UI frameworks.

2023 - 2024: Evolution

Driven by community requests and ourselves the need for a specific React component came up in 2023, and thus @typefox/monaco-editor-react was born. It was released as an independent npm package, and started as a thin React layer around monaco-editor-wrapper.

In 2024 all additional projects (monaco-editor-wrapper, @typefox/monaco-editor-react) moved into the main monaco-languageclient repository. The number of examples also increased. Further features and capabilities were added to all libraries in the project as needed. As the evolution of monaco-vscode-api continued independently, the number of features and services increased there as well.

The release frequency decreased after the fundamental changes of 2022. The last major releases of monaco-languageclient (v9) and monaco-editor-wrapper (v6) were performed shortly before the end of 2024. With the last major release of monaco-editor-wrapper it became possible to manage multiple language clients witin an application.

In this time period the outer wrapper library received much more attention and thereby more features. Much of the code of monaco-languageclient was removed due the architecture changes in 2022 already. Its focus was narrowed to supply the language client and to configure monaco-vscode-api.

Developer experience

During the last three years, the whole GitHub project served a dual purpose. We used it to validate the robustness of new technologies and thereby improving the overall developer experience. This is true for us, but for everyone else forking the GitHub project and using it.

We switched to vite for development and vitest for unit testing. We now use vitest’s browser mode together with playwright to write unit tests exactly how one would write them for a regular web app. On top of all that, the associated npm packages have been released as ESM only versions for over two years already.

Today: Version 10

If you wanted to create real applications just with the monaco-languageclient package you needed to write a lot of boiler plate code. monaco-editor-wrapper fulfilled the role of the application development toolkit. Earlier this year we realized that this separation is artificial. The idea was born to merge all useful things back into the monaco-languageclient package, so people can use one package, one toolbox for application development.

High level overview

The following sub-sections provide a high-level overview of the current architecture of monaco-languageclient. Afterwards, I illustrate what you can achieve with the toolbox and what could be next steps in the evolution of the monaco-languageclient package.

Before 2022 the monaco-languageclient library allowed you to use a single language client in monaco-editor. Today this is only one of several possible solutions. You can use further services (e.g. explorer view and output) from monaco-vscode-api to build a partial VS Code application.

Looking at it from a top-level perspective monaco-languageclient relies on the VS Code API supplied by monaco-vscode-api and it makes use of services supplied by many different npm packages.

monaco-languageclient relies on monaco-vscode-api for supplying API and services.

In the past, monaco-editor-wrapper introduced a combined, typed configuration (if you use TypeScript) for the monaco-vscode-api, for the language client configuration and for everything editor related. It proved to be a very useful approach and it was extended and altered over time. The downside was that the complete control flow was done internally and hidden from users.

A more approachable solution

Feedback received from & issues reported by the community, plus lessons we learned by using these libraries in practice drove the changes for monaco-languageclient version 10.

One major consideration we addressed was the need for separation of concerns. The biggest deviation from previous major versions is that the handling of monaco-vscode-api, the handling of language clients and the single editor app functionality are now clearly separated. Almost all useful pieces of code from the monaco-editor-wrapper package were moved to monaco-languageclient itself and the different functionalities are exposed via use-case-specific package sub-exports:

  • vscodeApiWrapper: Contains MonacoVscodeApiWrapper used to handle everything regarding monaco-vscode-api
  • lcwrapper: LanguageClientWrapper & LanguageClientsManager help to control one or multiple language clients
  • editorApp: EditorApp is used to control a single monaco-editor

The diagram below depicts all three sub-components:

monaco-languageclient's new sub-exports and their relations.

Separation of concerns and lifecycles

The user-facing API of the former approach (monaco-editor-wrapper) did not make it clear that components have different lifecycles, and also serve different purposes. The configuration used by monaco-editor-wrapper reflected the separation by its structure, but the handling was a black box to the end user. This turned out to be a non-ideal solution.

Now you have to configure and create the separate functionality exposed by the sub-exports, resulting in a bit more code, but it is directly clear what is done at which point in time. Testability of the code is thereby improved as well, which emphasizes the benefit of the chosen approach.

vscodeApiWrapper: It is mandatory to configure and initialize the provided services and functions of monaco-vscode-api first as this can only happen once during the application’s lifecycle (this is how VS Code works and we use a transformed version of it). However language servers and editors can be created and disposed as often as needed. So, instead of squeezing everything into one big configuration chunk, MonacoVscodeApiConfig is used to configure MonacoVscodeApiWrapper (vscodeApiWrapper). It initializes and starts monaco-vscode-api related features once and it must be done before everything else.

lcwrapper: Generally language clients’ life cycles are independent of editors’ lifecycles. Whenever a language client instance is initialized and started, it’s globally registered within monaco-vscode-api (like it would be within VS Code) to supply a service for a specific language, or specific document types.

With version 10, a LanguageClientConfig configures the LanguageClientWrapper. This existed almost identically with monaco-editor-wrapper, but is now contained here. When a client is disposed, the global registration is revoked. Afterwards, the editor can no longer supply language services for that specific language. Of course, a language client can be restarted and then supply the services again (if the server is still available). Usually there is not often a need to restart a language client during the application’s lifetime, but it is possible if required.

editorApp: The EditorApp and its EditorAppConfig are only needed to realize single monaco-editor applications. Editors can be created and removed as many times as needed. It is possible to have multiple instances of an EditorApp / monaco-editor in a single web application.

Further capabilities

If you want to build a ”partial” VS Code web-like application you can use ViewsService or the WorkbenchService available from monaco-vscode-api. This is an alternative way of supplying editors thus EditorApp is not required. monaco-languageclient contains functions to use the ViewsService directly without any need to write further code. monaco-vscode-api supplies many services allowing you to compose complex applications.

One additional package: @typefox/monaco-editor-react

It would have been possible to integrate some React component into monaco-languageclient or monaco-editor-wrapper, but we didn’t want to make any library dependent on a certain JavaScript UI framework. Instead we created @typefox/monaco-editor-react which wraps a React component shell around all other software components.

With version 7 of the package which was released simultaneously with monaco-languageclient version 10 MonacoEditorReactComp relies on the same configuration objects as you can see in the overview graphic below. The react component internally uses them to init and start the same classes.

@typefox/monaco-editor-react uses monaco-languageclient's sub-exports.

@typefox/monaco-editor-react is limited to configuring only one language client directly. The react component is best suited for single editor applications. Nonetheless, by design monaco-editor (and what we build around it) don’t fit well into React’s component architecture. We have built a reliable React component which capabilities are sufficient for most use cases of a React component. But, we think that support for more complex applications goes beyond the scope of the @typefox/monaco-editor-react package.

Editor application toolbox

This major release of monaco-languageclient changed the package to be more than a language client library. It is now a toolbox that contains all the tools you require to create fully functional editor centric web applications. All packages are MIT-licensed, i.e. they can be used even in commercial and closed-source projects.

Our demo examples in the repository already cover a variety of languages. They come without any need for a backend component on our GitHub Pages. Just to name a few, the examples demonstrate the usage of programming languages including Python, Java, Langium or C++. They show how existing language servers can be integrated into an editor centric web application with monaco-languageclient. Language servers are run directly, within web workers, or via Linux container images.

Application example: Langium Grammar DSL

While some examples are simple, others are more advanced, like the Python or Clangd Wasm cases. The python example provides a usable debugger, whereas the Clangd Wasm example demonstrates how thousands of header files can be synchronized between client and server.

Application example: Application Playground

The examples illustrate nicely what can be built, while real world applications usually go beyond that. We make creating language client applications for the web as simple as possible, but not simpler. The libraries and functionality they provide make language server protocol integration more approachable and as easy as possible for web applications.

What’s next?

This new release picked up many good ideas and refined them guided by community feedback and experience gained. After this major API refactoring we will focus on extensions and enhancements in the upcoming months.

Our agenda includes the following items:

  • A ready to use file-system synchronization mechanism that can be used in distributed applications parts could become part of the monaco-languageclient toolbox.
  • vscode-ws-jsonrpc has been there since the beginning, but it has not fundamentally changed. Now is a good time to think about how it can be evolved.
  • Further extensions geared toward application development (=create more re-usable code)
  • Further extend the documentation. Coinciding with this release we already updated the documentation, but that was only the first step.
  • Write more unit tests. In 2022 we had none. Now we have 67% test coverage. There is still need for improvement.

Conclusion

We are very pleased to see the development of monaco-languageclient from a project with uncertain future into a widely adapted Open Source Software project. The collaboration between Loïc and me both from different companies has been a success story. We will continue to supply you with building blocks for the next generation of web applications with sophisticated language support.

About the Author

Kai Salmen

Kai Salmen

Adapting new technologies, solving problems, and making things work well together with the team of people defines best what Kai’s work has always been about. From 3D computer graphics to space ground control systems and now language engineering tools he acquired a diverse skill set over the last twenty years. Working fully remote lets Kai combine family and work in the most positive way.