Build Real-Time Collaborative Whiteboard with React & Socket.io

Want to add a collaborative whiteboard to your site? In this tutorial, we’ll build one from scratch using React, TypeScript, the Canvas API, and Socket.io. By the end, you’ll have a working whiteboard where multiple users can draw, erase, and drop sticky notes together in real time.
Here’s a quick demo of the finished result:
Visit my website to see the live version https://chadlinden.com/whiteboard
🛠️ Prerequisites
- React 16.8+ (Hooks)
- TypeScript (recommended)
- Node.js (v14+)
- Basic knowledge of Canvas and WebSockets
1. Project Setup
Install dependencies:
# Frontend
npm install react react-dom next socket.io-client
# Backend
npm install express socket.io cors
Your package.json
should look something like this:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"server": "node server.js"
}
2. Create the Whiteboard Component
Create components/Whiteboard.tsx
:
import React, { useRef, useState, useEffect } from 'react'
export default function Whiteboard() {
const canvasRef = useRef<HTMLCanvasElement>(null)
const [isDrawing, setIsDrawing] = useState(false)
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const rect = canvas.getBoundingClientRect()
canvas.width = rect.width
canvas.height = rect.height
}, [])
const startDrawing = (e: any) => {
e.preventDefault()
setIsDrawing(true)
const ctx = canvasRef.current!.getContext('2d')!
ctx.beginPath()
}
const draw = (e: any) => {
if (!isDrawing) return
e.preventDefault()
const ctx = canvasRef.current!.getContext('2d')!
const rect = canvasRef.current!.getBoundingClientRect()
const x = e.touches ? e.touches[0].clientX - rect.left : e.clientX - rect.left
const y = e.touches ? e.touches[0].clientY - rect.top : e.clientY - rect.top
ctx.lineTo(x, y)
ctx.stroke()
}
const stopDrawing = () => setIsDrawing(false)
return (
<canvas
ref={canvasRef}
onMouseDown={startDrawing}
onMouseMove={draw}
onMouseUp={stopDrawing}
onTouchStart={startDrawing}
onTouchMove={draw}
onTouchEnd={stopDrawing}
className="border border-gray-300 w-full h-96 touch-none"
/>
)
}
This gives you a simple single-user canvas.
3. Add Tools and UI
Enhance your whiteboard with:
- Pen / Eraser buttons
- Color picker
- Line width slider
- Clear button
Example toolbar snippet:
<button onClick={() => setTool('pen')}>✏️ Pen</button>
<button onClick={() => setTool('eraser')}>🧹 Eraser</button>
<input type="color" value={color} onChange={(e) => setColor(e.target.value)} />
<input type="range" min="1" max="20" value={lineWidth} onChange={(e) => setLineWidth(+e.target.value)} />
<button onClick={clearCanvas}>Clear</button>
4. Add WebSocket Collaboration
Create server.js
:
const express = require('express')
const http = require('http')
const socketIO = require('socket.io')
const cors = require('cors')
const app = express()
app.use(cors())
const server = http.createServer(app)
const io = socketIO(server, { cors: { origin: "*" } })
io.on('connection', (socket) => {
console.log('New user:', socket.id)
socket.on('drawing', (data) => {
socket.broadcast.emit('remote-drawing', data)
})
socket.on('clear', () => {
socket.broadcast.emit('remote-clear')
})
})
server.listen(3001, () => console.log('Server running on port 3001'))
Connect from the client:
import io from 'socket.io-client'
const socket = io('http://localhost:3001')
// Send
socket.emit('drawing', action)
// Receive
socket.on('remote-drawing', (data) => {
// apply to canvas
})
5. Sticky Notes & Rooms
- Add a
"note"
event to drop collaborative sticky notes - Implement
"join-room"
so users can share a private session
6. Production Considerations
- Debounce resize so drawings persist
- Throttle events (~60fps) for smoother performance
- Validate input server-side to avoid abuse
Deployment options:
- Frontend → Vercel or Netlify
- Backend → Railway, Render, or Fly.io
✅ Final Result
At this point, you’ll have a working collaborative whiteboard with:
- Real-time multi-user drawing
- Mobile/touch support
- Tools (pen, eraser, color, size, clear)
- Sticky notes & rooms
- Production-ready performance
👉Live demo: chadlinden.com/whiteboard
Wrap-Up
This project shows how far you can get with React + Canvas + Socket.io. Start with a simple canvas, then add real-time sync and collaboration features step by step.
Try extending it with undo/redo, infinite canvas, or even AI-powered doodle recognition.
Happy coding!