Write code together in real time. Create a room, share the link, and start collaborating — complete with live cursors, built-in chat, and voice communication.
I built LiveEdit because pair programming should be easier. Google Docs is great for prose, Monaco editor is great for code, but there's no great "code docs" tool that combines both with real-time collaboration.
In November 2024, I started exploring:
- Could I build a VS Code-like editor in the browser?
- Could I make it collaborative without setting up a complex server?
- Could I deploy it globally with low latency?
The answer was yes, thanks to Yjs CRDTs and Cloudflare Workers. Yjs handles the hard math: guaranteeing that two people can edit simultaneously and converge on the same state. Cloudflare Workers handles the hard infrastructure: running close to users worldwide with automatic scaling on Durable Objects.
This is my exploration into modern collaborative infrastructure. Every part is carefully chosen:
- React 19 for the UI (it's what I know)
- Monaco for the editor (it's what VS Code uses)
- TanStack Start for SSR (for fast first-load)
- Cloudflare Workers for deployment (for global latency)
I'm not trying to replace Figma Lite or replace GitHub Codespaces. I'm trying to make pair programming just...work. No signup, no setup, just share a link.
Full IDE-like experience — Monaco editor, file explorer, integrated chat, and real-time collaboration
- Features
- Screenshots
- Architecture
- Tech Stack
- Getting Started
- Project Structure
- Deployment
- Testing
- Contributing
- License
- Instant Rooms — Create or join rooms via URL (
/room/:id). Each room is a fully isolated collaboration session with its own document state. - Conflict-Free Editing — Powered by Yjs CRDTs (Conflict-free Replicated Data Types), multiple users can edit the same file simultaneously without merge conflicts.
- Monaco Editor — The same editor that powers VS Code — full syntax highlighting, IntelliSense, multi-cursor support, and keyboard shortcuts.
- Per-File Document Isolation — Each file in the tree is backed by its own Yjs shared text type, so edits to one file never interfere with another.
- Live Cursors — See exactly where other users are typing with color-coded cursor labels.
- Selection Awareness — View collaborators' text selections in real time.
- Follow Mode — Click on a participant to follow their cursor and automatically switch to their active file.
- Typing Indicators — Know when teammates are actively editing.
- Integrated Chat — Built-in room chat synced via Yjs, with markdown support and relative timestamps.
- Voice Chat — WebRTC peer-to-peer audio with mute/unmute, deafen controls, and real-time speaking indicators.
- Participant List — See who's in the room, what file they're working on, and their online status.
- VS Code-Inspired UI — Activity bar, file explorer, tabbed editor, resizable panels, and a status bar — feels like home.
- File Explorer — Hierarchical tree view with file type icons and expand/collapse.
- Keyboard Shortcuts —
Ctrl+Bto toggle sidebar, plus all standard Monaco editor shortcuts. - Onboarding — First-time welcome modal guides new users through the interface.
- Edge Deployment — Runs on Cloudflare Workers globally with <50ms latency to most users.
- Durable Objects — Server-side session state with 24-hour document cache via Cloudflare Durable Objects.
- Auto-Reconnect — Exponential backoff WebSocket reconnection with connection status indicators.
- Guest Authentication — Instant access with auto-generated names and persistent identity via localStorage.
Landing Page & Authentication
| Sign In | Dashboard |
|---|---|
![]() |
![]() |
Sign in as a guest with an optional display name, or connect with Google OAuth. The dashboard lets you create new rooms or join existing ones by ID, with quick access to recent rooms.
Welcome & Onboarding
First-time visitors see an interactive onboarding modal that walks through the core features — file explorer, follow mode, chat, voice, and keyboard shortcuts.
Editor Interface
| Empty State | With Code |
|---|---|
![]() |
![]() |
The editor layout mirrors VS Code with an activity bar, collapsible sidebar, tabbed editor area, and a status bar showing connection status, cursor position, language, encoding, and room name.
Chat & Participants
| Integrated Chat | Participants Panel |
|---|---|
![]() |
![]() |
Chat with your team while editing. The participants panel shows who's online, what file they're working on, and lets you follow their cursor in real time.
LiveEdit is split into two independently deployed services on Cloudflare's edge network:
┌─────────────────────────────────────────────────────────┐
│ Client │
│ React 19 + TanStack Start (SSR) + Monaco + Yjs │
└──────────┬──────────────────────────────────┬───────────┘
│ HTTPS (SSR) │ WebSocket
▼ ▼
┌────────────────────────┐ ┌────────────────────────────┐
│ SSR App Worker │ │ Collaboration Worker │
│ (Cloudflare Workers) │ │ (Cloudflare Workers) │
│ │ │ │
│ • Server-side │ │ ┌──────────────────────┐ │
│ rendering │ │ │ Durable Object │ │
│ • Static assets │ │ │ (Session DO) │ │
│ • Route handling │ │ │ │ │
│ │ │ │ • Yjs sync protocol │ │
│ │ │ │ • WebSocket mgmt │ │
│ wrangler.toml │ │ │ • 24h doc cache │ │
│ │ │ │ • Awareness relay │ │
│ │ │ └──────────────────────┘ │
│ │ │ │
│ │ │ wrangler.collaboration. │
│ │ │ toml │
└────────────────────────┘ └────────────────────────────┘
- User joins a room — Client connects via WebSocket to the Collaboration Worker
- Durable Object routes connections to the same session by room ID
- Yjs sync protocol handles initial state sync and incremental updates
- Awareness protocol broadcasts cursor positions, selections, and user metadata
- Voice chat uses WebRTC with signaling over the Yjs awareness channel
Traditional operational transform (OT) systems require a central server to resolve conflicts. Yjs CRDTs resolve conflicts mathematically at each client, enabling:
- Offline editing — Changes merge cleanly when reconnected
- Low latency — No round-trip to a central authority needed
- Peer-to-peer ready — Can work without a server (via WebRTC)
- Guaranteed convergence — All clients reach the same state eventually
| Layer | Technology | Purpose |
|---|---|---|
| Framework | React 19 + TanStack Start | SSR-capable React with file-based routing |
| Editor | Monaco Editor | VS Code's editor component |
| Collaboration | Yjs | CRDT framework for real-time sync |
| Voice | WebRTC | Peer-to-peer audio communication |
| Styling | Tailwind CSS + shadcn/ui | Utility-first CSS with accessible components |
| UI Components | Radix UI + Lucide Icons | Headless accessible primitives |
| Virtualization | TanStack Virtual | Efficient rendering for chat messages |
| Panels | react-resizable-panels | Draggable, collapsible panel layout |
| Runtime | Cloudflare Workers | Edge-deployed serverless functions |
| State | Durable Objects | Strongly consistent session storage |
| Build | Vite 7 | Fast HMR and optimized production builds |
| Testing | Vitest + Playwright | Unit and end-to-end testing |
| Language | TypeScript 5.7 | Full type safety across the stack |
- Bun v1.0+ (recommended) or Node.js v18+
- Cloudflare account (for deployment)
# Clone the repository
git clone https://github.com/your-username/livedit-app.git
cd livedit-app
# Install dependencies
bun install
# Copy environment variables
cp .env.example .env.localbun run devThe app will be available at http://localhost:3000.
bun run buildlivedit-app/
├── src/
│ ├── routes/ # TanStack Router (file-based)
│ │ ├── __root.tsx # Root layout with providers
│ │ ├── index.tsx # Landing page (/)
│ │ └── room/
│ │ └── $roomId.tsx # Room page (/room/:id)
│ ├── components/
│ │ ├── layout/
│ │ │ ├── editor-layout.tsx # Main IDE layout with panels
│ │ │ ├── activity-bar.tsx # VS Code-style sidebar icons
│ │ │ ├── status-bar.tsx # Bottom status bar
│ │ │ └── loading-skeleton.tsx
│ │ ├── editor/
│ │ │ └── monaco-editor-with-presence.tsx
│ │ ├── file-tree/ # Hierarchical file explorer
│ │ ├── chat/ # Chat + voice controls
│ │ ├── presence/ # Cursors, user list, indicators
│ │ ├── onboarding/ # Welcome modal
│ │ └── ui/ # shadcn/ui primitives
│ ├── lib/
│ │ ├── yjs-doc.ts # Shared Yjs document & awareness
│ │ ├── websocket-provider.ts # Custom WebSocket provider
│ │ ├── voice-chat.ts # WebRTC voice implementation
│ │ ├── auth-context.tsx # Authentication state
│ │ ├── room-context.tsx # Room providers & sync setup
│ │ └── use-presence.ts # Presence hook
│ ├── hooks/ # React hooks
│ └── types/ # TypeScript type definitions
├── workers/
│ ├── collaboration.ts # Collaboration worker entry
│ └── durable-objects/
│ └── session.ts # SessionDurableObject (Yjs sync)
├── tests/ # E2E tests (Playwright)
├── scripts/
│ ├── deploy-worker.sh # Deploy collaboration worker
│ ├── deploy-app.sh # Deploy SSR app
│ ├── deploy-all.sh # Deploy everything
│ └── smoke-test.sh # Production smoke tests
├── docs/screenshots/ # README screenshots
├── wrangler.toml # SSR app config
├── wrangler.collaboration.toml # Collaboration worker config
└── DEPLOYMENT.md # Detailed deployment guide
LiveEdit is deployed on Cloudflare's edge infrastructure across 300+ locations worldwide.
# Authenticate with Cloudflare
bunx wrangler login
# Deploy both workers
./scripts/deploy-all.sh production# Deploy collaboration worker only
./scripts/deploy-worker.sh production
# Deploy SSR app only
./scripts/deploy-app.sh production
# Run smoke tests against production
./scripts/smoke-test.sh production| Variable | Location | Description |
|---|---|---|
ENVIRONMENT |
wrangler.toml |
production or staging |
WORKER_URL |
wrangler.toml |
Collaboration worker WebSocket URL |
ALLOWED_ORIGINS |
wrangler.collaboration.toml |
CORS allowed origins |
# Stream real-time logs from SSR worker
bunx wrangler tail --env production
# Stream logs from collaboration worker
bunx wrangler tail -c wrangler.collaboration.toml --env productionDashboard available at dash.cloudflare.com/workers.
# Run all unit tests
bun run test
# Run with interactive UI
bun run test:ui# Run Playwright E2E tests
bun run test:e2ebun run eslint| Decision | Rationale |
|---|---|
| Yjs over OT | CRDTs provide mathematical convergence guarantees without a central authority. Yjs has the best JavaScript ecosystem support. |
| Custom WebSocket provider | The built-in y-websocket provider didn't support Cloudflare Workers. Custom provider adds auto-reconnect with exponential backoff. |
| Durable Objects | Need co-located state (WebSocket connections + Yjs docs) in a single actor. Durable Objects provide exactly this with strong consistency. |
| Monaco over CodeMirror | Monaco provides the closest experience to VS Code. The y-monaco binding offers seamless Yjs integration. |
| WebRTC for voice | Peer-to-peer audio avoids the cost and latency of a media server. Awareness channel for signaling eliminates the need for a separate STUN/TURN server for small rooms. |
| TanStack Start | SSR with Cloudflare Workers support, file-based routing, and an active ecosystem. First-load performance is critical for collaboration tools. |
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Make your changes
- Test:
bun run test && bun run test:e2e - Lint:
bun run eslint - Submit a pull request
Please ensure all tests pass and the build succeeds before submitting.
This project is licensed under the MIT License — see the LICENSE file for details.
Built with Yjs + Monaco on Cloudflare Workers






