Skip to content

Repository files navigation

CollabEdit - Real-Time Collaborative Text Editor

A Google Docs-style real-time collaborative text editor where multiple users can simultaneously edit the same document with live cursor tracking, presence indicators, and conflict-free synchronization.

Live Demo

  • Frontend: [Add deployed URL]
  • Backend: [Add deployed URL]
  • Video Demo: [Add YouTube/Google Drive link]

Table of Contents


Tech Stack

Layer Technology
Frontend React 18, TypeScript, Vite, Tailwind CSS
Editor TipTap v3 (ProseMirror-based), y-prosemirror
Real-Time Yjs (CRDT), y-websocket, y-protocols, WebSocket (ws)
Backend Node.js, Express, TypeScript, tsx
Database PostgreSQL with Prisma ORM
Logging Pino (structured JSON logging)
Monorepo pnpm workspaces
Icons Lucide React
HTTP Client Axios
AI OpenAI GPT-4o-mini (writing assistant)

Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                      Client (React + Vite)              │
│                                                         │
│  ┌──────────┐  ┌───────────┐  ┌──────────────────────┐  │
│  │ Dashboard │  │  Editor   │  │  Presence Indicators │  │
│  │  (CRUD)   │  │ (TipTap)  │  │   (Avatar Stack)     │  │
│  └─────┬─────┘  └─────┬─────┘  └──────────┬───────────┘  │
│        │              │                    │              │
│        │         ┌────┴────┐               │              │
│        │         │   Yjs   │───────────────┘              │
│        │         │  (CRDT) │                              │
│        │         └────┬────┘                              │
└────────┼──────────────┼──────────────────────────────────┘
         │ REST API     │ WebSocket
         │ (Axios)      │ (y-websocket)
         ▼              ▼
┌─────────────────────────────────────────────────────────┐
│                   Server (Express + ws)                  │
│                                                         │
│  ┌────────────┐  ┌─────────────────────────────────┐    │
│  │  REST API   │  │     WebSocket Server             │    │
│  │ /api/*      │  │  - Sync protocol (Yjs)          │    │
│  │ - Documents │  │  - Awareness protocol (cursors)  │    │
│  │ - Users     │  │  - Room management               │    │
│  │ - Revisions │  │  - Debounced persistence         │    │
│  │ - AI ◆──────┼──┼───────────► OpenAI API           │    │
│  └──────┬──────┘  └──────────────┬──────────────────┘    │
│         │                        │                       │
│         └────────┬───────────────┘                       │
│                  ▼                                       │
│         ┌────────────────┐                               │
│         │  Prisma ORM    │                               │
│         └───────┬────────┘                               │
└─────────────────┼───────────────────────────────────────┘
                  ▼
         ┌────────────────┐
         │  PostgreSQL     │
         │  - Users        │
         │  - Documents    │
         │  - Revisions    │
         └────────────────┘

How Real-Time Sync Works

  1. CRDT-based conflict resolution: The editor uses Yjs, a high-performance CRDT (Conflict-free Replicated Data Type) implementation. Every client maintains a local Yjs document that merges changes automatically without conflicts.

  2. WebSocket transport: Clients connect via y-websocket to the server's /collab/:documentId endpoint. The server maintains in-memory "rooms" per document, each holding a Yjs document instance.

  3. Sync protocol: On connection, the server sends the full document state (SyncStep1). Subsequent edits are broadcast as incremental Yjs updates to all other connected clients, excluding the origin.

  4. Awareness protocol: Cursor positions, user names, and colors are shared through Yjs awareness, enabling live cursor tracking and presence indicators.

  5. Persistence: Document state is debounced (2 seconds) and saved to PostgreSQL as a serialized Yjs binary. Periodic snapshots are created every 50 updates for revision history. When the last client disconnects, a final save is triggered before the room is destroyed.


Features

  • Real-time collaborative editing - Multiple users edit simultaneously with instant sync
  • Live cursor tracking - See other users' cursors with colored indicators
  • User presence - Avatar stack showing who is online in a document
  • Rich text formatting - Bold, italic, underline, strikethrough, inline code, headings (H1/H2), bullet lists, ordered lists, blockquotes
  • Document management - Create, rename, delete documents from a dashboard
  • Revision history - Browse past revisions and restore documents to earlier states
  • Conflict-free editing - CRDT guarantees eventual consistency without manual conflict resolution
  • Persistent storage - Documents are saved to PostgreSQL and survive server restarts
  • AI Writing Assistant - Built-in AI panel powered by OpenAI:
    • Summarize - Condense selected text or the full document
    • Improve Writing - Enhance clarity, readability, and flow
    • Continue Writing - AI generates the next sentences matching your style
    • Fix Grammar - Correct spelling, grammar, and punctuation
    • Change Tone - Rewrite in Professional, Casual, Friendly, Academic, or Concise tone
    • Translate - Translate text to Spanish, French, German, Hindi, Japanese, Chinese, or Tamil
  • Lazy-loaded editor - Editor page is code-split for faster initial load
  • Responsive UI - Clean, modern interface built with Tailwind CSS

Project Structure

collab-editor/
├── client/                    # React frontend (Vite)
│   ├── src/
│   │   ├── components/
│   │   │   ├── common/        # Button, LoadingSpinner, Modal
│   │   │   ├── documents/     # DocumentList, DocumentCard, CreateDocumentModal
│   │   │   ├── editor/        # Editor, Toolbar, AIPanel, RevisionPanel, EditorFooter
│   │   │   ├── layout/        # AppLayout, Header, Sidebar
│   │   │   └── presence/      # AvatarStack, PresenceIndicator
│   │   ├── hooks/
│   │   │   ├── useAI.ts              # OpenAI-powered writing assistant
│   │   │   ├── useCollaboration.ts   # Yjs doc + WebSocket provider setup
│   │   │   ├── useDocuments.ts       # Document CRUD operations
│   │   │   ├── usePresence.ts        # Awareness-based user presence
│   │   │   ├── useRevisions.ts       # Revision history fetch/restore
│   │   │   └── useUser.ts            # User registration and session
│   │   ├── lib/
│   │   │   ├── api.ts                # Axios instance with auth headers
│   │   │   └── colors.ts             # User color/initials utilities
│   │   ├── pages/
│   │   │   ├── DashboardPage.tsx      # Document listing + creation
│   │   │   ├── EditorPage.tsx         # Collaborative editor view
│   │   │   └── NotFoundPage.tsx       # 404 page
│   │   └── App.tsx                    # Root component with routing
│   ├── vercel.json                    # SPA rewrite rules for Vercel
│   └── vite.config.ts                 # Vite config with chunk splitting
│
├── server/                    # Express + WebSocket backend
│   ├── src/
│   │   ├── config/env.ts              # Environment variable loading
│   │   ├── middleware/errorHandler.ts  # Express error middleware
│   │   ├── routes/
│   │   │   ├── ai.ts                  # AI writing assistant endpoint
│   │   │   ├── documents.ts           # Document CRUD endpoints
│   │   │   ├── revisions.ts           # Revision history + restore
│   │   │   ├── users.ts               # User registration + lookup
│   │   │   └── index.ts               # Route aggregator
│   │   ├── services/
│   │   │   ├── aiService.ts           # OpenAI integration logic
│   │   │   ├── documentService.ts     # Document business logic
│   │   │   ├── revisionService.ts     # Revision business logic
│   │   │   └── userService.ts         # User business logic
│   │   ├── utils/
│   │   │   ├── prisma.ts             # Prisma client singleton
│   │   │   └── logger.ts             # Pino logger instance
│   │   ├── websocket/
│   │   │   ├── server.ts             # WebSocket server, rooms, sync
│   │   │   └── persistence.ts        # Debounced DB persistence
│   │   └── index.ts                  # Express app entry point
│   └── prisma/
│       └── schema.prisma             # Database schema
│
├── packages/
│   └── shared/                # Shared TypeScript types and constants
│       └── src/
│           ├── types/         # Document, User type definitions
│           └── constants/     # Shared constants
│
├── pnpm-workspace.yaml        # Monorepo workspace config
├── .env.example               # Environment variable template
└── package.json               # Root scripts (dev, build)

Setup Instructions

Prerequisites

  • Node.js >= 18
  • pnpm (install via npm install -g pnpm)
  • PostgreSQL running locally or a hosted instance

1. Clone the Repository

git clone https://github.com/<your-username>/collab-editor.git
cd collab-editor

2. Install Dependencies

pnpm install

3. Configure Environment Variables

Copy the example env files and fill in your values:

# Root-level (reference)
cp .env.example .env

# Server
cp server/.env.example server/.env

# Client
cp client/.env.example client/.env

Edit server/.env with your PostgreSQL connection string and OpenAI API key (see Environment Variables).

4. Set Up the Database

# Generate Prisma client
pnpm --filter server db:generate

# Run database migrations
pnpm --filter server db:migrate

5. Build the Shared Package

pnpm --filter shared build

6. Start the Development Servers

# Start both client and server concurrently
pnpm dev

Or start them individually:

pnpm dev:server   # Express + WebSocket on http://localhost:3001
pnpm dev:client   # Vite dev server on http://localhost:5173

7. Open the App

Navigate to http://localhost:5173 in your browser. Enter a name and email to get started, then create a document and start editing. Open a second browser tab/window to see real-time collaboration in action.


Environment Variables

Server (server/.env)

Variable Description Default
DATABASE_URL PostgreSQL connection string postgresql://user:password@localhost:5432/collab_editor
PORT Server port 3001
CLIENT_URL Frontend URL (for CORS) http://localhost:5173
NODE_ENV Environment mode development
OPENAI_API_KEY OpenAI API key (for AI assistant) (required for AI features)

Client (client/.env)

Variable Description Default
VITE_API_URL Backend REST API base URL http://localhost:3001/api
VITE_WS_URL Backend WebSocket URL ws://localhost:3001

API Endpoints

Users

Method Endpoint Description
POST /api/users Register / login a user
GET /api/users/me Get current user (by x-user-id header)

Documents

Method Endpoint Description
GET /api/documents List user's documents
POST /api/documents Create a new document
GET /api/documents/:id Get document by ID
PATCH /api/documents/:id Update document title
DELETE /api/documents/:id Delete a document
POST /api/documents/:id/join Join a document as a collaborator

Revisions

Method Endpoint Description
GET /api/documents/:id/revisions List document revisions
GET /api/documents/:id/revisions/:revId Get a specific revision
POST /api/documents/:id/revisions/:revId/restore Restore to a revision

AI

Method Endpoint Description
POST /api/ai AI writing assistant. Body: { action, text, tone?, language? }

Supported actions: summarize, improve, continue, fix-grammar, change-tone, translate

WebSocket

Endpoint Description
ws://<host>/collab/:documentId Yjs sync + awareness protocol for real-time editing

WebSocket Protocol

The WebSocket server implements two Yjs sub-protocols:

  • Sync Protocol (msg type 0): Handles document state exchange. On connect, the server sends SyncStep1 (state vector). The client responds with SyncStep2 (missing updates). Subsequent edits are broadcast as incremental updates.

  • Awareness Protocol (msg type 1): Carries user presence data (name, color, cursor position). Updates are broadcast to all connected clients in the same room.

A heartbeat ping/pong runs every 30 seconds to detect and clean up stale connections.


AI Tools Used

In-App: OpenAI GPT-4o-mini

The editor includes a built-in AI Writing Assistant panel powered by OpenAI's GPT-4o-mini model. Users can select text (or use the full document) and invoke AI actions directly from the editor toolbar. The server proxies requests to the OpenAI API via the /api/ai endpoint.

Development: Claude (with Ollama)

Claude was used as the primary AI coding assistant during development, running locally via Ollama for privacy and offline access. It assisted with:

  • Architecting the monorepo structure and choosing the CRDT approach over OT
  • Designing the WebSocket room management and persistence strategy
  • Writing and debugging the Yjs integration with TipTap v3 (ProseMirror plugin bridging)
  • Structuring the Prisma schema for documents, users, and revision history
  • Code review and refactoring suggestions

Development: Codex (OpenAI)

Codex was used as a supplementary AI tool for:

  • Generating boilerplate code for Express routes and React components
  • Autocompleting repetitive patterns (Prisma service functions, React hooks)
  • Writing TypeScript type definitions for the shared package
  • Suggesting Tailwind CSS class combinations for UI components

Deployment Guide

The application is split into two deployable units:

Part Platform Reason
Client Vercel Free, fast static/SPA hosting; already has vercel.json
Server Render Free tier, supports WebSockets + long-running processes
Database Render PostgreSQL Provision alongside the server with zero extra config

Why not Vercel for the server? The server uses persistent WebSocket connections for real-time collaboration. Vercel Functions are request/response-based and do not support long-lived WebSocket connections.

Step 1: Deploy PostgreSQL + Server on Render

  1. Create a Render account and go to the Dashboard.

  2. Create a PostgreSQL database:

    • Dashboard > New > PostgreSQL
    • Name: collab-editor-db
    • Plan: Free
    • Click Create Database
    • After creation, copy the Internal Database URL
  3. Deploy the server as a Web Service:

    • Dashboard > New > Web Service
    • Connect your GitHub repository
    • Configure the service:
    Setting Value
    Name collab-editor-api
    Root Directory server
    Runtime Node
    Build Command pnpm install && pnpm build
    Start Command pnpm start
    • Add the following Environment Variables:
    Variable Value
    DATABASE_URL (paste the Internal Database URL from step 2)
    CLIENT_URL https://your-app.vercel.app (update after Vercel deploy)
    PORT 10000
    NODE_ENV production
    OPENAI_API_KEY (your OpenAI API key)
  4. Click Create Web Service. After deployment completes, note the service URL (e.g. https://collab-editor-api.onrender.com).

The start script (prisma migrate deploy && node dist/index.js) automatically runs pending database migrations on every deploy.

Step 2: Deploy Client on Vercel

  1. Create a production env file at client/.env.production:

    VITE_API_URL=https://collab-editor-api.onrender.com/api
    VITE_WS_URL=wss://collab-editor-api.onrender.com
    

    Use wss:// (not ws://) in production — Render provides TLS automatically.

  2. Option A — Deploy via Vercel Dashboard:

    • Go to vercel.com and import your GitHub repository
    • Set Root Directory to client
    • Framework Preset: Vite
    • Add environment variables: VITE_API_URL and VITE_WS_URL with the Render server URLs
    • Click Deploy
  3. Option B — Deploy via CLI:

    npm i -g vercel
    cd client
    vercel

    When prompted, confirm the root directory and it will detect vercel.json automatically.

  4. After deploy, copy the Vercel URL (e.g. https://collab-editor.vercel.app) and go back to Render to update the CLIENT_URL environment variable so CORS allows requests from your frontend.

Step 3: Verify the Deployment

  1. Open the Vercel URL in two browser tabs
  2. Register as two different users
  3. Create a document and open it in both tabs
  4. Type in one tab — changes should appear in the other in real time
  5. Click the AI button in the toolbar and test a feature like Summarize

Alternative: Railway (Single Platform)

If you prefer hosting everything in one place, Railway supports both Node.js services and PostgreSQL with WebSocket support:

  1. Create a new project on Railway
  2. Add a PostgreSQL plugin — copy the DATABASE_URL
  3. Add a Node.js service, set root directory to server, and configure the same env vars
  4. Deploy the client separately on Vercel (or use Railway's static site hosting)

Known Limitations

  1. No authentication/authorization: Users are identified by a simple name + email registration stored in localStorage. There is no password, OAuth, or JWT-based auth. Any user can access any document by ID.

  2. Single-server WebSocket: The WebSocket server holds document rooms in memory. This means horizontal scaling (multiple server instances) is not supported without adding a pub/sub layer (e.g., Redis) for cross-instance synchronization.

  3. No offline editing: If the WebSocket connection drops, edits are not queued locally. The user must be online for changes to sync. Yjs supports offline-first patterns, but this is not implemented.

  4. Basic revision history: Revisions are stored as Yjs binary blobs. The UI shows a list of revisions with timestamps but does not display a visual diff between versions. Restoring a revision requires a page reload to see the changes.

  5. No granular permissions: All users who join a document have full edit access. There are no read-only viewers, document ownership restrictions, or sharing permissions.

  6. No rate limiting: The REST API and WebSocket server do not enforce rate limits, making them vulnerable to abuse in a production environment.

  7. User session is per-browser: User identity is stored in localStorage, so it does not persist across different browsers or devices. Clearing browser data loses the session.

  8. No rich media support: The editor supports text formatting only. Images, tables, embeds, and file attachments are not supported.

  9. Debounced persistence: Document changes are saved to the database with a 2-second debounce. If the server crashes within this window, the most recent edits may be lost.


Scripts Reference

Command Description
pnpm dev Start client + server concurrently in dev mode
pnpm dev:client Start only the Vite dev server
pnpm dev:server Start only the Express server (with hot reload)
pnpm build Build shared package, then client and server
pnpm --filter server db:migrate Run Prisma migrations
pnpm --filter server db:generate Regenerate Prisma client
pnpm --filter server db:studio Open Prisma Studio (DB browser)

License

This project was built as a submission for the HCL x GUVI hackathon.

About

Real-time collaborative editor built with a pnpm monorepo (React+Vite client, Express server, shared types). Uses Yjs + TipTap for CRDT-based editing with cursors and presence. Prisma + PostgreSQL for persistence and revisions. Includes dashboard, document CRUD, and a clean Tailwind UI.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages