CoDevTogether is a real-time collaborative coding workspace built with React 19, Vite, Express, and Socket.IO. Create an invite link, edit code with teammates, share problem notes, chat, track presence, and compile code without leaving the room.
Live application: codev.abhishekchorotiya.xyz
- Shared CodeMirror editor with live room synchronization
- JavaScript, Python, Java, and C++ language modes
- Shared problem statement and notes
- Real-time participant presence and reconnect handling
- Room chat and server-generated activity messages
- Remote compilation with timeout and error reporting
- Ocean, Ember, and Midnight themes
- Mobile workspace tabs and off-canvas participant drawer
- Resizable desktop code, output, and chat panels
- Keyboard-accessible dropdowns, tabs, and resize controls
- Lazy-loaded room and editor bundles
| Area | Stack |
|---|---|
| UI | React 19, React Router, Tailwind CSS, Lucide |
| Editor | CodeMirror 6 |
| Build | Vite 8 |
| Server | Node.js, Express 5 |
| Collaboration | Socket.IO 4 |
| Tests | Vitest, Testing Library, jsdom |
| Quality | ESLint, npm audit |
flowchart LR
A[React client] <-->|Room events| B[Socket.IO server]
B <--> C[In-memory room store]
A -->|POST /api/compile| B
B -->|Proxy request| D[OneCompiler client API]
B --> E[Built Vite application]
The server binds each socket to its validated room and keeps one authoritative document snapshot while that room is active. A newly joined participant receives that snapshot once, avoiding peer-to-peer synchronization races. Code edits use last-write-wins synchronization; a CRDT would be the next step for conflict-free simultaneous editing.
- High-Level Design — system context, deployment, data flows, security boundaries, scaling, and failure handling
- Low-Level Design — modules, state models, HTTP and socket contracts, validation limits, UI behavior, and testing
- Node.js 20.19 or newer
- npm 10 or newer
Install dependencies and create the frontend environment file:
npm install
cp .env.example .envStart the collaboration server:
npm run serverIn another terminal, start Vite:
npm run devOpen http://localhost:5173. During development, Vite proxies Socket.IO traffic to http://localhost:5000.
Vite reads these values from .env when it starts:
| Variable | Required | Description |
|---|---|---|
VITE_COMPILER_API_URL |
No | Alternate compiler proxy URL; leave empty to use this server's /api/compile route |
VITE_SOCKET_URL |
No | Socket.IO URL; leave empty for the development proxy or same-origin production |
Pass server variables through the process environment:
| Variable | Default | Description |
|---|---|---|
PORT |
5000 |
HTTP and Socket.IO port |
SOCKET_ALLOWED_ORIGINS |
Same host and localhost | Comma-separated additional browser origins |
Example:
SOCKET_ALLOWED_ORIGINS=https://app.example.com PORT=5000 npm startThe client sends POST /api/compile to the Express server with JSON:
{
"code": "console.log('Hello')",
"language": "javascript",
"fileName": "main.js"
}The server validates the request and forwards it to the OneCompiler client endpoint using this payload shape:
{
"properties": {
"language": "javascript",
"files": [
{
"name": "main.js",
"content": "console.log('Hello')"
}
],
"stdin": null
}
}The browser receives the compiler response, including stdout and stderr:
{
"stdout": "Hello\n",
"stderr": ""
}Browser requests are aborted after ten seconds and upstream server requests after nine seconds. The proxy accepts only supported languages, limits code to 200 KB, caps upstream responses, and rate-limits compilation requests per client.
| Command | Purpose |
|---|---|
npm run dev |
Start the Vite development server |
npm run server |
Start the backend in watch mode |
npm start |
Start the production server |
npm run build |
Build the frontend into dist/ |
npm run preview |
Preview the Vite production build |
npm test |
Run the test suite once |
npm run test:watch |
Run tests in watch mode |
npm run lint |
Check the codebase with ESLint |
Build the frontend, then start the Express server:
npm ci
npm run build
npm startExpress serves dist/, provides GET /health, and returns the React application for browser routes. Configure your reverse proxy for WebSocket upgrades on /socket.io/.
server/
compilerProxy.js Validated OneCompiler client API proxy
index.js HTTP and Socket.IO entry point
socketHandlers.js Validation and room event handlers
roomStore.js Authoritative active-room state
src/
app/ Application routing and tests
features/
chat/ Chat interface
editor/ CodeMirror and language metadata
lobby/ Join-room experience
room/ Workspace, panels, and room session hook
shared/
components/ Reusable accessible controls
hooks/ Shared React hooks
protocol/ Client/server event contract
services/ Compiler and socket clients
utils/ Room ID helpers
styles/ Global styles and theme tokens
docs/
HLD.md High-level system design
LLD.md Low-level implementation design
Run the complete local quality gate:
npm run lint
npm test
npm run build
npm auditThe tests cover lobby validation, compiler payloads, room storage, and protection against cross-room socket injection.
- Room IDs are invite links, not authentication. Add user authentication and room authorization before storing private code.
- Socket payloads are type-checked, size-limited, rate-limited, and restricted to the sender's joined room.
- Active room state is held in memory and removed when the last participant leaves. Restarts do not preserve rooms.
- A single server instance is currently assumed. Horizontal scaling requires a shared room store and a Socket.IO adapter such as Redis.
- Simultaneous edits use last-write-wins behavior rather than operational transforms or a CRDT.
- The compiler proxy depends on OneCompiler's client endpoint and should be monitored for upstream contract changes.
Create a focused branch, keep changes covered by tests, and run the verification commands before opening a pull request. When modifying socket events, update the shared protocol contract and test both authorized room behavior and cross-room isolation.