A real-time collaborative document editor built with React 19, Socket.IO, Quill 2, and MongoDB.
Multiple users can simultaneously edit the same document with changes propagated in real-time via WebSockets and automatically persisted to the database.
Note: This project was originally built in 2021 and has since undergone a full modernization, migrating from JavaScript to TypeScript, CRA to Vite, React 17 to 19, and restructuring both client and server into a clean, production-grade architecture. See REFACTORING.md for the complete breakdown of every change and the reasoning behind it.
| Layer | Technology |
|---|---|
| Client | React 19, TypeScript 5, Vite 6, Quill 2, Socket.IO |
| Server | Node.js, TypeScript 5, Socket.IO 4.8, Mongoose 8 |
| Database | MongoDB 8 |
| Tooling | Docker Compose, tsx, ESLint |
client/ server/
├── src/ ├── src/
│ ├── components/ │ ├── config/ # Env validation (Zod)
│ │ └── TextEditor.tsx │ ├── handlers/ # Socket event handlers
│ ├── hooks/ │ ├── models/ # Mongoose schemas
│ │ ├── useSocket.ts │ ├── types/ # Shared type definitions
│ │ └── useDocumentSync │ └── index.ts # Entry point
│ ├── config/ ├── tsconfig.json
│ ├── types/ └── package.json
│ ├── styles/
│ ├── App.tsx
│ └── main.tsx
├── vite.config.ts
├── tsconfig.json
└── package.json
The fastest way to run the entire project. A single command spins up MongoDB (with seed data), the Node.js server, and the Vite client:
docker compose upOnce all three containers are running, open http://localhost:5173.
The database is automatically seeded with two sample documents on first launch:
| URL | Description |
|---|---|
| /documents/welcome | A feature overview document |
| /documents/getting-started | A keyboard shortcuts guide |
To start fresh, remove the volume and re-run:
docker compose down -v && docker compose upIf you prefer running without Docker:
- Node.js 20+
- A running MongoDB instance
# Server
cd server && npm install
# Client
cd client && npm installCopy the example env files and adjust if needed:
cp server/.env.example server/.env
cp client/.env.example client/.env# Terminal 1 - Server
cd server && npm run dev
# Terminal 2 - Client
cd client && npm run devOpen http://localhost:5173. The app generates a unique document URL on load. Share the URL with another browser tab to see real-time collaboration.
| Script | Description |
|---|---|
npm run dev |
Start dev server with hot reload (tsx watch) |
npm run build |
Compile TypeScript to dist/ |
npm start |
Run compiled production build |
npm run typecheck |
Type-check without emitting |
| Script | Description |
|---|---|
npm run dev |
Start Vite dev server |
npm run build |
Type-check and build for production |
npm run preview |
Preview production build |
npm run typecheck |
Type-check without emitting |
| Command | Description |
|---|---|
docker compose up |
Start all services (MongoDB + server + client) |
docker compose up -d |
Start all services in the background |
docker compose down |
Stop all services |
docker compose down -v |
Stop all services and remove database volume |
docker compose build |
Rebuild container images |
- The client connects to the server via Socket.IO
- On visiting
/documents/:id, the server retrieves or creates the document in MongoDB - The Quill editor loads the document content
- User keystrokes emit
send-changesevents containing Quill Delta operations - The server broadcasts deltas to all other clients in the same document room
- An auto-save interval persists the full document state every 2 seconds
MIT License - Copyright (c) 2021 Rami Atrouni