velum is a real-time collaborative text editor built for shared writing. Multiple users can open the same document, type at the same time, see each other's presence, and keep working without overwriting each other.
Real-time Collaborative Text Editor
- Live URL: here!
- GitHub:
https://github.com/ziennaa/velum
Velum focuses on one thing: making a shared document actually feel live.
Users can:
- create and open documents
- edit the same document from multiple browser windows
- see presence indicators
- see coloured collaborator cursors
- use basic rich-text formatting
- rely on document persistence
- browse revision history
This is a shared workspace demo build. Anyone with the document link can open it and collaborate. That choice was intentional for the hackathon build so the focus stayed on real-time editing, conflict-free sync, and product flow rather than authentication.
This project was built to match the required problem statement:
- Real-time synchronisation of text changes across multiple users
- User presence indicators
- Cursor position tracking per user
- Conflict resolution when multiple users edit the same section
- Basic text formatting: bold, italic, underline
- Document persistence and revision history
- WebSocket-based real-time communication
- CRDT-based collaborative editing
- Node.js backend
- React frontend
- MongoDB document storage
- React
- TypeScript
- Vite
- Tailwind CSS
- Tiptap
- Node.js
- Express
- TypeScript
- Hocuspocus
- Yjs
- MongoDB
- Mongoose
- Frontend: Vercel
- Backend: Railway
The project has two main parts:
The client is a React app that renders the landing page, dashboard, and editor UI.
The editor uses Tiptap for rich-text editing and connects to the collaboration backend through a Yjs/Hocuspocus provider. Presence, cursors, and syncing are all driven from that collaboration connection.
The server exposes:
- REST endpoints for document and revision management
- a Hocuspocus WebSocket server for collaborative editing
- MongoDB persistence for documents and revision snapshots
- A user opens a document
- The client connects to the Hocuspocus WebSocket server
- Yjs manages shared document state as a CRDT
- Remote edits merge without manual conflict handling
- The server stores document state and revision history in MongoDB
velum/
├── client/ # React + Vite frontend
└── server/ # Node.js + Hocuspocus + MongoDB backend
┌─────────────────────────────────────────────────────────────┐
│ Browser │
│ React + Tiptap + Yjs + HocuspocusProvider │
└─────────────────────────┬───────────────────────────────────┘
│ HTTP (REST API) + WebSocket
┌─────────────────────────▼───────────────────────────────────┐
│ Node.js Server (PORT 3001) │
│ │
│ HTTP → Express (REST API: documents, revisions) │
│ WS → Hocuspocus (Yjs sync + presence awareness) │
└─────────────────────────┬───────────────────────────────────┘
│ Mongoose
┌─────────────────────────▼───────────────────────────────────┐
│ MongoDB │
│ documents: { title, yjsState (Binary), timestamps } │
│ revisions: { documentId, yjsState, contentPreview } │
└─────────────────────────────────────────────────────────────┘
The HTTP server and WebSocket server share one port. When a client connects via ws://, the Node.js upgrade event hands the socket to Hocuspocus. Regular HTTP requests go to Express as normal.
Yjs encodes document state as a binary CRDT. Hocuspocus loads it from MongoDB when the first user opens a document, and saves it back every 2 seconds of inactivity. Revision snapshots are written separately on a 30-second throttle.
This project uses Yjs, a CRDT library, instead of writing custom merge logic. Collaborative editing becomes complex quickly when multiple users edit the same content simultaneously. A CRDT-based model ensures a consistent shared state without forcing users to manually resolve conflicts.
In most basic collaborative tools, if two users edit the same sentence simultaneously, the person who saves last overwrites the other. Data Loss: Edits are simply discarded based on timestamp. Poor UX: Users must wait for "turns" to avoid overwriting each other.
Velum implements Conflict-free Replicated Data Types (CRDTs) to ensure that every user's intent is preserved, regardless of network latency or sync order.
- Main repo: https://github.com/ziennaa/velum
| Layer | Technology |
|---|---|
| Editor | Tiptap v2 |
| CRDT | Yjs |
| WebSocket | Hocuspocus |
| Frontend | React + Vite + TypeScript + Tailwind CSS |
| Backend | Node.js + Express |
| Database | MongoDB + Mongoose |
- Node.js 20+
- npm
- MongoDB Atlas connection string
You need Node.js 18+ and a MongoDB connection string (local or Atlas free tier).
git clone https://github.com/ziennaa/velum.git
cd velum
cd server && npm install
cd ../client && npm installCreate server/.env:
PORT=3001
MONGODB_URI=<your_mongodb_connection_string>
CLIENT_URL=http://localhost:5173
NODE_ENV=development
Run the backend:
npm install
npm build dev
npm run dev
Open another terminal:
cd client
npm install
Create client/.env:
VITE_API_URL=http://localhost:3001
VITE_WS_URL=ws://localhost:3001
Run the frontend:
npm run dev
The frontend should run on http://localhost:5173.
- New project → deploy from GitHub → set root directory to
server - Build command:
npm install && npm run build - Start command:
npm start - Add environment variables:
MONGODB_URI,CLIENT_URL,NODE_ENV=production - Railway sets
PORTautomatically
- Import repo → set root directory to
client - Framework preset: Vite
- Add environment variables:
VITE_API_URL= your Railway URLVITE_WS_URL= your Railway URL withwss://prefix
Every document is accessible to anyone with its URL. There are no accounts, no access control, and no ownership. This is intentional — the project is about real-time collaboration, not authentication. Anyone who has the link can open the document and start editing immediately.
- No authentication. Any URL is publicly editable.
- Revision restore requires a page reload to reflect the restored state.
- The free Railway tier sleeps after inactivity. The first request after sleep takes ~5 seconds.
- Mobile layout works but the editor toolbar is compact on small screens.
- ChatGPT
- Claude
Claude (Anthropic) was used for architectural guidance, debugging. All integration decisions, design choices, and final implementation were verified and adjusted manually.
| Criterion | Implementation |
|---|---|
| Real-time sync | Yjs + Hocuspocus WebSocket, character-by-character |
| Conflict resolution | Yjs CRDT — deterministic merge, no overwrite |
| Presence indicators | Awareness protocol, coloured cursors, name labels |
| Cursor tracking | CollaborationCursor extension, per-user colour |
| Formatting | Bold, italic, underline, headings, lists, code |
| Persistence | MongoDB, yjsState binary field |
| Revision history | 30-second snapshots, panel UI, one-click restore |
| WebSocket | Single-port HTTP upgrade, Hocuspocus protocol |
Released under the MIT License. See LICENSE.