Table of contents
Code editors feel like magic when they know what you mean. That magic — the red squiggles, the autocomplete, the ability to CMD+click into a function definition — comes from a Language Server Protocol (LSP) implementation running in the background.
For desktop IDEs this is straightforward: the editor spawns a child process running the TypeScript language server binary. In a browser, there are no child processes. So we had to get creative to build WebSandbox.
Web Workers to the Rescue
Modern browsers support Web Workers — background threads that run JavaScript without blocking the main UI thread. In WebSandbox, we run the TypeScript compiler API entirely inside a dedicated Worker, communicating with the editor via a custom message-passing protocol that mirrors LSP's JSON-RPC spec.
We manage the lifecycle of this worker through a dedicated useIDEWorkerSync React hook in our architecture. This hook initializes a TSWorkerClient that talks directly to the worker. It also includes robust fallback logic—if the initial worker hydration (which loads seed files) fails, the client gracefully retries with a clean state rather than crashing the editor. This resilient worker management is a core part of our proprietary engine.
The CodeMirror 6 Integration
Our editor leverages @uiw/react-codemirror, a robust React wrapper around CodeMirror 6. We built an advanced LSP client extension that:
- Sends
textDocument/didChangenotifications on every keystroke. - Maps CodeMirror positions to LSP line/character positions and vice versa.
- Translates LSP
Diagnosticobjects into CodeMirror lint markers using@codemirror/lint.
Additionally, to provide an advanced, Git-integrated review experience within the browser, we utilize react-codemirror-merge. This allows us to render inline and side-by-side diffs just like a native desktop editor, mapping changes seamlessly against our local persistence layer.
Virtual File System Sync
The TypeScript language server needs to know about all files in the project, not just the one currently open. We solve this by mirroring the WebContainer's file system into the LSP worker's in-memory environment, powered by @typescript/vfs.
To keep the worker performant, we carefully track which files actually need syncing. Using a utility called isWorkerTrackedFile, our sync layer filters out irrelevant files (like node_modules binaries or compiled output) and maintains an active set of paths via a workerPathsRef. When a file is modified, our custom event bus propagates the change, allowing the compiler host to instantly offer cross-file type intelligence without choking on unnecessary data.
Proprietary Polish
Building a truly professional IDE in the browser requires moving past basic integrations and building custom, highly optimized glue layers. Our proprietary LSP architecture means you get full TypeScript intelligence working entirely within your browser tab.
Completion latency averages under 80ms on a modern laptop, which is imperceptible to most users. Diagnostics are re-run on a 300ms debounce to avoid thrashing the Worker during rapid typing. The result is an experience that matches desktop performance without ever leaving the browser.
