I built a code editor library because Monaco frustrated me

I built a code editor library because Monaco frustrated me

I built a code editor library because Monaco frustrated mePFMCODES

I built a code editor library because Monaco frustrated me Monaco was slow, buggy, and the...

I built a code editor library because Monaco frustrated me

Monaco was slow, buggy, and the syntax highlighting wasn't even that good. So I built my own.

Caret is a lightweight code editor library built on the EditContext API. 605 lines of code. ~32KB. Loads in ~25ms.


Why not CodeMirror or Monaco?

Monaco was my go-to for other projects. But after dealing with slow load times, sync bugs, and unreliable highlighting, I decided to start from scratch. The first version shipped in a few days. The current version is a complete rewrite — cleaner, faster, and a fraction of the size.

Here's how it compares:

Caret Monaco CodeMirror 6
Bundle size ~32KB ~5MB ~400KB
Load time ~25ms ~2-3s ~500ms
Lines of code 650 ~300,000 ~50,000
Architecture EditContext textarea contenteditable

Load time on Chrome

~25ms load

Load time on Edge

~27ms load


The architecture

Previous versions used a textarea-based input model, which caused constant caret synchronization issues. The current version (v0.3+) is built entirely on the EditContext API — a browser API designed specifically for custom editors. It gives direct control over text input with no hacks.

How it works under the hood:

  1. EditContext receives all keyboard input, IME, and clipboard events
  2. A single string text is the source of truth — no dual-layer sync issues
  3. render() calls hljs.highlight() and updates the DOM
  4. The caret is positioned via Range.getBoundingClientRect() — no canvas math
  5. Undo/redo is a pure string stack storing both content and cursor offset

The codebase is split into five focused modules:

  • textEditor.js — core editor logic (400 lines)
  • caret.js — pixel-perfect caret positioning via Range API
  • lineCounter.js — line number tracking
  • font.js — custom font loading
  • languages.js — Highlight.js language registration

Features

  • Live syntax highlighting via Highlight.js
  • Custom caret rendering via Range API
  • Smart indentation — Tab/Shift+Tab for code blocks
  • Full undo/redo stack with cursor position restoration
  • Arrow key navigation with column preservation
  • Read-only lock mode
  • Multiple editor instances on the same page
  • Custom font support
  • Plain text paste — no rich HTML bleed
  • Browser detection with a friendly error for unsupported browsers

Themes supported: Tokyo Night, Monokai, GitHub Dark, Atom One Dark, Nord, Night Owl, VS2015, and more.

Languages supported: JavaScript, Python, HTML, and anything Highlight.js covers.


Quick start

Quick Start.gif

npm install @pfmcodes/caret
Enter fullscreen mode Exit fullscreen mode
<!DOCTYPE html>
<html>
<head></head>
<body>
  <div id="editor"></div>
  <script type="module">
    import Caret from 'https://esm.sh/@pfmcodes/caret';

    const editor = await Caret.createEditor(
      document.getElementById('editor'),
      '// Start coding...',
      {
        id: 'my-editor',
        dark: true,
        language: 'javascript',
        hlTheme: 'tokyo-night-dark'
      }
    );
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Browser support

Caret v0.3+ uses the EditContext API, which is currently only available in Chromium-based browsers (Chrome, Edge). Firefox and Safari are not supported yet — Firefox tracking issue here.


Links