A full-stack, real-time collaborative code editor built with:
| Layer | Technology |
|---|---|
| Frontend | React 18 + Vite |
| Editor | Monaco Editor (VS Code's engine) |
| Real-time CRDT | Yjs |
| WebSocket server | Hocuspocus |
| REST API | Node.js + Express |
| Database | PostgreSQL |
| Auth | JWT (bcrypt + jsonwebtoken) |
- Real-time collaboration — multiple users edit the same document simultaneously; all changes merge conflict-free via Yjs CRDTs.
- Live cursors & selections — see every collaborator's cursor in a unique colour, powered by the Yjs Awareness protocol.
- Syntax highlighting for 20+ languages (JavaScript, TypeScript, Python, Rust, Go, SQL, …)
- Team Spaces — organise documents into shared workspaces; invite collaborators by email.
- Persistent documents — Hocuspocus stores the binary Yjs state in PostgreSQL so documents survive server restarts.
- Auto-detected language from the file extension (e.g.
main.py→ Python). - Inline title editing — click the document name in the header to rename it instantly.
- Clean VS Code-inspired dark UI with a resizable sidebar.
notepad-online/
├── backend/
│ ├── src/
│ │ ├── server.js # Express REST API (port 3001)
│ │ ├── collaboration.js # Hocuspocus WS server (port 1234)
│ │ ├── db.js # PostgreSQL pool
│ │ ├── middleware/
│ │ │ └── auth.js # JWT middleware
│ │ └── routes/
│ │ ├── auth.js # POST /api/auth/register|login, GET /me
│ │ ├── spaces.js # CRUD for Spaces + member management
│ │ └── documents.js # CRUD for Documents
│ ├── schema.sql # PostgreSQL schema
│ ├── package.json
│ └── .env.example
├── frontend/
│ ├── src/
│ │ ├── App.jsx
│ │ ├── main.jsx
│ │ ├── index.css # Global dark theme
│ │ ├── api/
│ │ │ └── client.js # Axios instance
│ │ ├── contexts/
│ │ │ └── AuthContext.jsx
│ │ └── components/
│ │ ├── Auth/
│ │ │ └── AuthPage.jsx
│ │ ├── Layout/
│ │ │ ├── MainLayout.jsx
│ │ │ └── Sidebar.jsx
│ │ └── Editor/
│ │ └── CollaborativeEditor.jsx ← the core
│ ├── package.json
│ ├── vite.config.js
│ └── .env.example
├── docker-compose.yml # PostgreSQL via Docker
└── .gitignore
- Node.js 18+ and npm 9+
- PostgreSQL 14+ (or Docker)
Option A — Docker (recommended)
docker compose up -dThis starts PostgreSQL on localhost:5432 and auto-runs schema.sql.
Option B — Local PostgreSQL
psql -U postgres -c "CREATE DATABASE notepad_online;"
psql -U postgres -d notepad_online -f backend/schema.sqlcd backend
cp .env.example .env # edit DB credentials and JWT secret
npm install
npm run dev # starts both API (3001) and WS (1234) serversThe dev script uses concurrently to run both:
http://localhost:3001— REST APIws://localhost:1234— Collaboration WebSocket
cd frontend
cp .env.example .env # optional: only if ports differ
npm install
npm run devOpen http://localhost:5173 in your browser.
Browser A Hocuspocus Server Browser B
───────── ───────────────── ─────────
Y.Doc ──── HocuspocusProvider ──► onAuthenticate (JWT)
─► Database.fetch() (load from PG)
│
▼
◄──────────── Yjs binary state ────────────────►
MonacoBinding MonacoBinding
(Monaco ↔ Y.Text) (Monaco ↔ Y.Text)
User A types → Yjs encodes the change as an "update"
→ Update is broadcast to all connected clients
→ Browser B's Y.Doc applies the update
→ MonacoBinding reflects the change in Monaco
→ Browser B sees Browser A's text instantly
On disconnect → Hocuspocus calls Database.store()
→ Full Y.Doc state is saved to PostgreSQL as BYTEA
→ Next open: Database.fetch() restores the state
The Yjs Awareness protocol carries each user's cursor position and colour. y-monaco's MonacoBinding automatically renders coloured cursor lines and selection highlights for every connected collaborator.
| Method | Path | Body | Description |
|---|---|---|---|
| POST | /api/auth/register |
{username, email, password} |
Create account |
| POST | /api/auth/login |
{email, password} |
Login → JWT |
| GET | /api/auth/me |
— (Bearer token) | Current user |
| Method | Path | Description |
|---|---|---|
| GET | /api/spaces |
List user's spaces |
| POST | /api/spaces |
Create space |
| DELETE | /api/spaces/:id |
Delete space (owner only) |
| POST | /api/spaces/:id/members |
Invite user by email |
| GET | /api/spaces/:id/members |
List members |
| Method | Path | Description |
|---|---|---|
| GET | /api/documents/spaces/:spaceId |
List documents in space |
| POST | /api/documents/spaces/:spaceId |
Create document |
| GET | /api/documents/:id |
Get document metadata |
| PATCH | /api/documents/:id |
Update title / language |
| DELETE | /api/documents/:id |
Delete document |
| Variable | Default | Description |
|---|---|---|
DB_HOST |
localhost |
PostgreSQL host |
DB_PORT |
5432 |
PostgreSQL port |
DB_NAME |
notepad_online |
Database name |
DB_USER |
postgres |
Database user |
DB_PASSWORD |
postgres |
Database password |
JWT_SECRET |
(required) | Secret for signing JWTs |
API_PORT |
3001 |
REST API port |
COLLAB_PORT |
1234 |
WebSocket server port |
FRONTEND_URL |
http://localhost:5173 |
Allowed CORS origin |
| Variable | Default | Description |
|---|---|---|
VITE_API_URL |
/api (proxied) |
REST API base URL |
VITE_COLLAB_URL |
ws://localhost:1234 |
Hocuspocus WS URL |
| Package | Why |
|---|---|
@hocuspocus/server |
WebSocket server that implements the Hocuspocus protocol for Yjs |
@hocuspocus/extension-database |
Persistence plugin — fetch/store binary Yjs state from any DB |
@hocuspocus/provider |
Client-side WebSocket connector for Yjs |
yjs |
The CRDT library powering conflict-free merging of concurrent edits |
y-monaco |
Binds a Y.Text CRDT to a Monaco editor model + renders awareness cursors |
@monaco-editor/react |
React wrapper for Monaco Editor (the engine behind VS Code) |
- Change
JWT_SECRETto a strong random string (openssl rand -hex 64) - Use HTTPS/WSS — wrap both servers behind nginx with TLS
- Set
FRONTEND_URLto your production domain in backend.env - Use a managed PostgreSQL service (AWS RDS, Supabase, Neon, …)
- Run Node.js processes with a process manager (PM2, systemd)
- Consider rate-limiting the auth endpoints to prevent brute-force
- Version history (Yjs
UndoManageror snapshot table) - Document chat sidebar
- Granular roles (viewer/editor/owner) enforced in the editor
- Export as
.txt,.md, or.pdf - AI code suggestions via Claude API
- File tree with folders within a space