opensourceprojects.dev

A broadsheet for software that doesn't ask for your email

Monaco Editor with Shiki highlighting, no web workers or CSS loaders
GitHub RepoImpressions2

Project Description

View on GitHub

A Monaco Editor Setup That Doesn't Make You Fight Web Workers

If you've ever tried to drop Monaco Editor into a project, you know the drill. You set up MonacoEnvironment, configure web workers, wire up a CSS loader, and spend an afternoon debugging why your language services aren't loading. It works, eventually, but the setup is a chore. Modern Monaco is an attempt to strip all of that away.

What It Does

Modern Monaco is a modernized wrapper around Monaco Editor (the editor that powers VS Code). It handles the parts of Monaco that are usually annoying to configure, so you don't have to think about them. No MonacoEnvironment setup. No web workers to wire up. No CSS loaders to configure.

It uses Shiki for syntax highlighting instead of Monaco's built-in tokenizer, which means you get access to Shiki's extensive collection of grammars and themes. The architecture is built around lazy loading: while monaco-editor-core is loading in the background, Shiki pre-highlights your code so users see something meaningful immediately instead of a blank editor. It also supports server-side rendering, workspace features like edit history and a file system provider, and automatic loading of .d.ts files from the esm.sh CDN for type checking.

It resolves bare specifier imports in JavaScript and TypeScript using import maps, supports embedded languages (importmap, CSS, JavaScript) inside HTML, inline HTML and CSS in JavaScript/TypeScript, and auto-closing HTML/JSX tags. It also exposes VS Code window APIs like showInputBox and showQuickPick.

Why It's Cool

  • The setup problem is real, and this solves it. Monaco is a great editor, but its bundler and worker configuration is a known pain point. Modern Monaco's pitch is simple: install it, use it, don't think about workers. That's a meaningful quality-of-life improvement for anyone building a browser-based editor.

  • Lazy mode is a clever approach to perceived performance. Instead of showing a loading spinner while Monaco boots up, you pre-highlight the code with Shiki and swap it out when the editor is ready. The user sees formatted code almost instantly. This is the kind of detail that matters when you're embedding an editor in a production app.

  • SSR support opens up new use cases. Being able to render a mock editor on the server and hydrate it on the client means you can ship meaningful HTML on first paint. The README's example shows this working in a Cloudflare Worker-style fetch handler, which is a nice demonstration of how flexible the setup is.

  • Type checking without the headache. Automatically pulling .d.ts files from esm.sh for type checking is a small feature that saves a lot of manual work. If you're building a playground or documentation site, this removes a step you'd otherwise have to build yourself.

  • Import maps for bare specifiers. Resolving bare imports in JS/TS is something most editors punt on. Modern Monaco handles it via import maps, which is the right primitive for the job in a browser context.

  • Workspace abstraction. The Workspace object lets you manage editor models without touching Monaco's native APIs directly. It gives you a higher-level interface for file operations and opening documents, which is friendlier than working with raw Monaco models.

How to Try It

You can install it from NPM:

npm i modern-monaco

Or import it directly from esm.sh without a build step:

import * from "https://esm.sh/modern-monaco"

To create an editor in lazy mode, add a <monaco-editor> custom element to your HTML and call the lazy function:

<!-- index.html -->
<monaco-editor></monaco-editor>
<script src="app.js" type="module"></script>
// app.js
import { lazy, Workspace } from "modern-monaco";

const workspace = new Workspace({
  initialFiles: {
    "index.html": `<html><body>...</body></html>`,
    "main.js": `console.log("Hello, world!")`,
  },
  entryFile: "index.html",
});

lazy({ workspace });

await workspace.fs.writeFile("util.js", "export function add(a, b) { return a + b; }");
workspace.openTextDocument("util.js");

For SSR mode, you can render a web component on the server and hydrate it on the client. The README includes a working example using renderToWebComponent and a hydrate call on the client side.

There's also a demo you can check out at https://modern-monaco-demo.vercel.app.

Full documentation and source are at https://github.com/esm-dev/modern-monaco.

Final Thoughts

Modern Monaco is under active development, and the README is upfront about that—the API may change at any time. That's worth taking seriously if you're considering it for production. But for developers who want a Monaco editor without the usual configuration overhead, it's a promising option. The combination of Shiki highlighting, lazy loading, and SSR support covers a lot of ground that most Monaco wrappers ignore. If you're building a code playground, documentation site, or any app that needs an embedded editor, it's worth a look.

Back to Projects
Project ID: ca8c8f61-8ffb-4b67-8c68-007f5d633169Last updated: September 17, 2026 at 02:47 AM