days since this article was written, please be aware of its timeliness
I’ve been working at Replit for about six years now. As the team has grown, my focus has remained on the IDE portion of our product (which we call the “Workspace”). Naturally, this has led me to concentrate increasingly on the code editor. While we’ve considered building a code editor tailored to our needs, the complexity of developing one, the abundance of available open-source options, and the size of our team make it a venture fraught with unknowns. Thus, my time likely won’t be spent on constructing our own editor from scratch. I’ve had the privilege (and pain) of using three code editors—Ace, Monaco, and CodeMirror—in production environments, sometimes simultaneously (more on that later). In this article, I’ll revisit some of Replit’s history and my experiences with these editors, as well as how the two have interacted.
If you’re reading this solely for a direct comparison of these three code editors, feel free to skip to the end, where I’ve placed the summary and comparison.
The Story
Prelude: Ace
In Replit’s early days, around 2011, there was no code editor. We used a pure REPL interface—a console with a simple input box. Adding a code editor was clearly an urgent need, especially if we wanted to enable the creation of more complex programs. A code editor would provide syntax highlighting, editor shortcuts, auto-indentation, search-and-replace, and other essential features. At the time, Cloud9 had released Ace, a fully featured, high-performance web-based code editor. To this day, Ace remains actively maintained; it boasts a rich ecosystem, supports multiple languages and keybindings, and performs well in the browser.
👇🏻 A comparison of Replit’s original interface and the Ace code editor. This screenshot was provided by community members who, starting in 2011, rehosted Replit’s open-source version https://www.repldotit.com.

We had been using Ace until around 2017 when we switched to Monaco. Although Ace is still being maintained, it has only one maintainer. After Amazon acquired Cloud9, they seemed to reprioritize their open-source projects. The editor no longer receives as many updates as before, issues on GitHub began to pile up, and the maintainers hardly added any new features. Overall, Ace’s API started to feel outdated and cumbersome to developers. Its maintainer is great, but there’s only so much one person can do.
Interlude: Monaco
As you might already know, VSCode uses the Monaco editor; in fact, Microsoft built VSCode around Monaco. By switching our code editor to Monaco, we believed we could leverage all the cool updates and features from VSCode’s development experts. Monaco offers a sleek, modern UI that aligns better with our website, provides excellent autocomplete for JavaScript, HTML, and CSS, and appears to have APIs that make it easy to write language clients for LSP (Language Server Protocol). Their API documentation is excellent, comes with TypeScript definitions, and offers more extensibility features from a development perspective.
👇🏻Screenshot of the Monaco editor (default configuration), note the clean and tidy UI.

Switching from Ace to Monaco comes at a cost, as the latter lacks many features of Ace. However, we believe that with the community’s enthusiasm and contributions, it will soon surpass Ace. The first issue with Monaco (compared to Ace) is its lack of support for many languages. Even though VSCode supports numerous languages, this relies on NodeJS/Electron capabilities rather than the browser. Therefore, we began contributing code to Monaco to expand its language support. We added support for languages like Scala, Julia, Scheme, and Clojure, as well as bug fixes for languages such as Python. I wrote a syntax highlighter for Monaco to support all languages Ace supports via an adapter. Finally, the missing feature in Monaco compared to Ace is Vim and Emacs keybindings, though it likely won’t be long before someone publishes an NPM package to address this.
Another issue with Monaco is its build tooling. Although Microsoft built Monaco using web technologies, it doesn’t integrate well with the current web ecosystem and build tools. I had to precompile Monaco into a Webpack Dll and add numerous Webpack configurations just to make it work. These additions were painful and increased the complexity and cost of our build system. Months later, Microsoft released a Webpack plugin for Monaco, which improved things slightly, but it’s still not perfect—especially after we migrated our frontend to Next.js. Unfortunately, Monaco also lacks a straightforward way to lazy-load and split code, making it impossible to break the code into smaller files for faster loading. This issue bloated our project by up to 5MB, which isn’t something we can easily ignore.
Monaco also performs poorly on mobile. We tried outsourcing this part of the code, but no one took the job. Then I considered handling it myself, only to realize how difficult it is to navigate and understand Monaco’s codebase. It’s tightly coupled with other parts of the VSCode codebase, as if the Monaco editor itself was extracted from VSCode after the latter was built. Even VSCode’s code isn’t particularly well-written—it’s likely one of the oldest TypeScript projects and was developed in Microsoft’s corporate style. While we managed to create a somewhat functional mobile version, I didn’t want to maintain a fork of VSCode, as our modifications wouldn’t be merged into the main branch, and the current mobile implementation is far from production-ready. Thus, I decided the best approach was to continue using the Ace editor on mobile. It may not be perfect, but it’s good enough (it works, after all).
As a result, we ended up with two code editors on Replit: one for desktop and another for mobile. Every new feature for the desktop had to be ported to Ace (the mobile version). We had to write a language client with LSP support for Ace, and we had to implement an Operational Transformation (OT) adapter for Ace to enable multi-user collaborative editing, among other things. In many cases, we simply didn’t have time to port features. For example, we never brought the multi-threading functionality to mobile.
Behind the Scenes: CodeMirror
At the end of 2018, Marijn announced a rewrite of CodeMirror to version 6, accompanied by an excellent design document. One of the main motivations for the rewrite was to add support for touch devices. Around this time, we recognized that mobile (in the broadest sense) was a critical part of our strategy; if we wanted to bring the next billion software creators online, we had to make mobile viable. CodeMirror would achieve mobile usability by leveraging the browser’s native text editing capabilities (via contenteditable) rather than handling everything at the JavaScript/library level (like Google Docs with Google Closure).
The API design of CodeMirror 6 was inspired by ProseMirror, another project by Marijn. I had used it before in a WYSIWYG project I was working on at the time, and I loved it. ProseMirror has a tiny core, with everything else implemented through its plugin system. As an editor library, it’s modular, pluggable, functional, and incredibly empowering. So, I decided to have the company sponsor the development of the new CodeMirror, and I even personally funded the project.
Last year, CodeMirror 6 entered beta, and I was thrilled to start integrating it into Replit—as was the rest of the team. We began experimenting with the editor, and while the learning curve was steep, once you finally “got” it, you felt like a super-code-editor-developer. To make CodeMirror 6 viable for the project, I started introducing it incrementally. We first added it as a read-only editor on Replit, then began incorporating it into other parts of the site where code could be edited.
Earlier this year, we embarked on the “Leap of Faith” (translator’s note: a reference to Assassin’s Creed) by integrating CodeMirror into our mobile environment. From a user perspective, CodeMirror is objectively superior to any other editor on mobile devices. Although it doesn’t yet support all the languages and some other features we offer, integrating it into mobile was still worthwhile. The user feedback following this integration has been even better than we anticipated. Among users participating in CodeMirror usage (translator’s note: possibly through small-scale testing or by providing a toggle between old and new versions to compare editor usage), nearly 70% were mobile users! It (CodeMirror) retains users better than using Ace on mobile. With CodeMirror’s pluggability (offering limitless possibilities), it’s clear that these (encouraging results) are just the beginning of delivering more valuable features on mobile. We’ll start by porting missing PC features to the mobile version of CodeMirror.

The CodeMirror 6 community is still in its early stages, so we had to write many things ourselves or sponsor specific features and work with Marijn to fix bugs. We hope our contributions will help guide and give back to the CodeMirror community. Here’s a list of features we’re actively developing: Vim Mode, Emacs Mode, LSP client, indent markers, CSS color picker, language parsers, and many other things we’ll announce in future posts when we release the desktop version of CodeMirror. I believe people are excited about the new CodeMirror, and we’ll see a surge in community and ecosystem adoption over the next year or two, with many eager to use it in production.
We’re thrilled to build more and more features on CodeMirror and make it an indispensable part of our mission to make code more accessible. We’ve always said that eventually, we’d need to develop our own editor to craft the user experience the way we want. That said, I think we’re very happy with what we’ve been able to achieve using CodeMirror.
The Battle Section
Let me summarize to make it easier for you to find the right code editor for your needs. Again, this is just my personal experience, and it may not reflect yours.
For each category, I’ll rate the editors from 1 to 3, where 3 is the best.
Stability
| Editor | Rating | Description |
|---|
| Ace | ⭐️⭐️⭐️ | Extremely stable and reliable. This editor has stood the test of time, providing support for many tools for over a decade. Throughout my usage, I’ve never experienced any breaking changes. Some versions may have introduced minor bugs, but they were quickly fixed. |
| Monaco | ⭐️⭐️ | Monaco offers a stable editing experience, though it does have some bugs. These are typically fixed quickly with VSCode releases, as its maintainers excel at rolling out frequent updates. I deducted one star because its API isn’t the most stable—minor changes occur often, which can be annoying. Microsoft has yet to release a 1.0 version of Monaco. |
| CodeMirror 6 | ⭐️ | CodeMirror is still in beta. The project has many minor bugs, but Marijn responds and fixes them rapidly. Although it remains in beta, I believe Marijn is satisfied with the current API, making major breaking changes unlikely. CodeMirror 6 is being adopted by many features, and even Chrome’s DevTools might start using it as a code editor next year. |
Out-of-the-Box Experience
| Editor | Rating | Description |
|---|---|---|
| Ace | ⭐️⭐️ | Excellent out-of-the-box experience with support for numerous features and languages, including basic JavaScript Lint functionality (using JSHint) and auto-completion. The UI feels slightly dated, which may occasionally make it seem cluttered. |
| Monaco | ⭐️⭐️⭐️ | Highly polished user interface. The editor comes packed with features, including out-of-the-box code hinting support for HTML, CSS, and JavaScript. |
| CodeMirror 6 | ⭐️⭐️ | Requires some configuration to work properly. This is a trade-off for its modular project architecture. A basic-setup package combines several modules and re-exports core ones. The UI is quite good. |
Modularity, Build Tools, and Development Trajectory
| Editor | Rating | Description |
|---|---|---|
| Ace | ⭐️⭐️ | Ace itself is quite small and supports modularity, allowing you to lazily load other modules. However, Ace is a relatively old project that comes with its own module management system. While it’s not difficult to make it work in your application, some configuration is required. |
| Monaco | ⭐️ | The Monaco editor also has a very large bundle size, around 5M. As far as I know, lazy loading is not possible. Monaco also requires special configuration in your build system, making it challenging to integrate with existing systems. |
| CodeMirror 6 | ⭐️⭐️ | CodeMirror is built with modern technology. You can even use ES6 modules to import it without involving a bundler. Lazy loading is effortless; you just need ES6 dynamic imports. The project is highly modular, and the editor core is very small. |
Extensibility and Advanced Features
| Editor | Rating | Description |
|---|
| Ace | ⭐️⭐️ | Ace offers numerous configuration options with excellent effects and well-designed extension points. While not universally applicable, it enables the implementation of many advanced features. The API does feel somewhat dated, but remains absolutely reliable. When I need to quickly prototype a project, I effortlessly choose Ace because its code is highly readable, and its core code has remained virtually unchanged for nearly a decade. |
| Monaco | ⭐️⭐️ | Monaco provides extensive configuration options and APIs to modify editor behavior and core functionality. However, its extensibility remains limited and overly specific. I frequently find myself wrestling with the editor, resorting to hacky patches—a risky and unstable approach since its codebase isn’t easily readable and undergoes constant internal changes. Ultimately, we stopped upgrading Monaco as it’s unlikely to ever support certain features we require. |
| CodeMirror 6 | ⭐️⭐️⭐️ | From its inception, CodeMirror 6 was designed with extensibility as one of its core principles. This modular approach allows CodeMirror to be highly adaptable. In fact, the core itself (@codemirror/view and @codemirror/state) is essentially an extensible textarea. All “code” functionalities are implemented as extensions. Even basic features like syntax highlighting and line numbers are delivered through extensions and packages. These official packages serve as excellent reference materials when creating your own extensions. Building fancy plugins with CodeMirror is a breeze—it offers plugin developers incredibly powerful support! Its extension points are so versatile that you become the god of your programming universe, free to implement any feature you desire (and bear the consequences). |
Community and Documentation
| Editor | Rating | Description |
|---|---|---|
| Ace | ⭐️⭐️ | Over the years, Ace has built a rich ecosystem with numerous articles and blogs about its usage. It supports syntax highlighting for virtually every language under the sun and offers many community-provided toolkits. Its API documentation isn’t the best but suffices for most use cases. Its well-structured (though slightly dated) codebase serves as an excellent supplementary resource. There’s also an online getting-started guide available. |
| Monaco | ⭐️⭐️ | Monaco started gaining momentum around 2018, but its community has noticeably declined since then. You can find a bunch of community-maintained packages on NPM. Monaco’s API documentation is already quite good, but it could be better. The lack of an official guide makes it challenging for beginners to get started. Due to the project’s complex structure, reading the source code as a supplement to the API docs is difficult. |
| CodeMirror 6 | ⭐️⭐️⭐️ | I’ve observed strong development momentum for CodeMirror 6 in the community. We’re working on some packages we believe are crucial for CodeMirror 6 to help grow the ecosystem—stay tuned! The documentation is excellent, and I expect it to improve further over time. The docs feature a great getting-started guide and numerous examples with detailed explanations. As mentioned earlier regarding extensibility, most of its functionality is implemented via extensions, making these packages invaluable “dream teammates” when building features. |
Performance
Disclaimer: Performance evaluations are not based on explicit benchmark data.
| Editor | Rating | Description |
|---|---|---|
| Ace | ⭐️⭐️⭐️ | Ace was born in an era when browsers and machines were far less powerful than today, so its performance remains impressive now. |
| Monaco | ⭐️⭐️ | Monaco has many performance optimizations, but it can feel somewhat bloated. Replit has many users on low-power devices who consistently report Monaco as being power-hungry. |
| CodeMirror 6 | ⭐️⭐️⭐️ | CodeMirror has demonstrated excellent performance so far, as its author has invested significant effort in optimization. |
Mobile Support
I won’t provide ratings here because if you need a mobile-friendly code editor, CodeMirror 6 is currently your only viable option. Ace’s mobile support isn’t terrible but isn’t production-ready, while Monaco doesn’t work on mobile at all.
I would even say that CodeMirror can be applied in native applications using Webview components. Most content in CodeMirror is serializable, so you can interact with the Webview from native code.
Thanks for reading!
This section is the author’s advertisement time and will not be translated for now.
I often wish that when facing some key decisions in life, someone could tell me the best course of action so that I would not waste my precious time. Putting myself in others' shoes, I therefore write blogs often, hoping to record in this tiny corner of the vast Internet the once-in-a-lifetime experiences that matter to me, and to help those who seek help.