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!