“Build a code editor” is the meta-version of the rich-text-editor question — the same content-editable challenges plus syntax-aware features (highlighting, autocomplete, lint, multi-cursor). Web-based code editors live inside CodeSandbox, Replit, Cursor, GitHub, and many SaaS apps. The interview tests whether you understand the tradeoffs between the major open-source editors and the integration work involved.
Clarify scope
- Read-only or full editing?
- Single-language or multi-language?
- Autocomplete from local symbols or LSP?
- Diff view, blame, inline annotations?
- Mobile support? (most code editors are desktop-first)
The big two: CodeMirror 6 vs Monaco
CodeMirror 6
- Modular, tree-shakable, modern (rewrite from CM5)
- Smaller bundle (~50KB minimum, grows with features)
- Tree-sitter or Lezer parsers for syntax
- Strong on customization; many extensions
- Used by Sourcegraph, Replit, Codepen
Monaco
- The VS Code editor extracted
- Larger bundle (~1MB)
- Built-in TypeScript / JS / JSON / CSS / HTML language services
- LSP-style completion, diagnostics, hover
- Used by GitHub Codespaces, CodeSandbox
Pick CodeMirror if you need to ship a small bundle and customize heavily. Pick Monaco if you want VS-Code-grade features out of the box and the bundle size is acceptable.
Other options
- Ace — older, mature, smaller community in 2026
- Lexical (with code blocks) — for embedded code in a doc, not a full editor
- Custom: only if you have unusual constraints (mobile-first, very small bundle)
Syntax highlighting
- Tokenization: turn source into typed tokens
- CodeMirror uses Lezer or tree-sitter; Monaco uses Monarch grammars or TextMate grammars
- Theming: token-type → color mapping; Light/Dark/HC themes
- Performance: incremental parsing on edit, not full re-parse
Autocomplete
Three tiers:
- Local: words from the current buffer (CodeMirror @codemirror/autocomplete)
- Language-aware: typescript-language-features for TS/JS in Monaco; LSP for many in CodeMirror
- AI-powered: stream completion from an LLM (Cursor, Copilot pattern)
Multi-cursor
Both editors support; the API differs. Cmd+D to add cursor at next match; Cmd+Click to add cursor at click. Implementation:
- Selection state is an array of ranges, not a single range
- Edits apply to all cursors
- Insertions handled per-cursor with offset adjustments
- UI: render multiple visible carets
LSP integration
Language Server Protocol for full IDE features:
- Run language server (e.g., typescript-language-server) somewhere — server, web worker, or via WebContainer
- JSON-RPC messaging between editor and language server
- Editor displays completions, diagnostics, hover, go-to-definition
- Latency budget: 100–300 ms per request
Performance considerations
- Tokenization on the main thread for short files; web worker for long ones
- Virtualize the line render: only visible lines mounted
- Avoid heavy React wrappers around per-line components
- Debounce expensive operations (lint, format) on type
Accessibility
- Both CodeMirror and Monaco have screen-reader modes; verify with NVDA / VoiceOver
- Keyboard navigation full
- High-contrast theme available
- Focus indicator and aria attributes
Mobile considerations
- Both editors handle mobile reasonably; Monaco is heavier
- On-screen keyboard interactions can be janky; test on real devices
- Touch selection differs from mouse; both editors support it
- For mobile-first products, consider a simplified custom editor
Diff view
- Both editors support side-by-side diff (Monaco built-in; CodeMirror via @codemirror/merge)
- Inline diff for code review UI
- Three-way merge view for conflict resolution
Collaboration
- Real-time multi-user editing requires CRDT (Yjs has bindings for both editors)
- Y.Text + ProseMirror or @codemirror/collab
- Cursor and selection broadcast via the same channel
What separates senior from staff
Senior candidates discuss CodeMirror vs Monaco tradeoffs and integrate one into a React app. Staff candidates discuss LSP integration, multi-cursor mechanics, performance under large files, and the bundle-size strategy. Principal candidates raise the collaborative-editing layer (CRDT) and the language-server hosting story (browser-based via web workers / WebContainer vs server-side).
Frequently Asked Questions
Can I use Monaco in mobile web?
Possible but heavy. The bundle and the on-screen keyboard interactions are challenging. CodeMirror is friendlier on mobile.
How do I add AI autocomplete?
Implement a CompletionSource that calls an LLM API. Stream tokens; insert as ghost text; commit on Tab. The Cursor / Copilot pattern.
Should I use a language server in the browser?
For TypeScript / JavaScript, yes — Monaco bundles it. For Python or other languages, run via WebContainer (StackBlitz) or server-side. Latency is the main concern.