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
Load time on Edge
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:
- EditContext receives all keyboard input, IME, and clipboard events
- A single string
textis the source of truth — no dual-layer sync issues -
render()callshljs.highlight()and updates the DOM - The caret is positioned via
Range.getBoundingClientRect()— no canvas math - 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
npm install @pfmcodes/caret
<!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>
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.



Top comments (3)
monaco editor is not just syntax highlighting, it fully powers vscode, all line formatting, code issues highlight
but i get where you are coming from and this is a very nice project, some of us don't actually need full monaco
i built it out of frustation of complexity, i admire speed and small bundle size, that's why i built caret. vs code uses extra modules for better syntax highlighting, i simply used highlight.js, strip of all that extra modules and code you have base monaco, and its syntax highlighting is not that good as they showcase on their demo website
It works