Building a Real-Time Collaborative Notepad with Socket.IO and MongoDB

Published: (January 2, 2026 at 12:50 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

Introduction

Ever needed to quickly share notes with someone but got frustrated with sign‑up forms and complex permission settings? Collaborative Notepad is a free, open‑source real‑time notepad that lets you create a note, share the link, and start collaborating instantly—no accounts required.

Features

  • No sign‑up required
  • Real‑time collaboration via Socket.IO
  • Custom URLs (e.g., /meeting-notes)
  • Dark/Light mode toggle
  • Auto‑save every 3 seconds
  • Live user count display

Live Demo

https://collabnote.link

Repository

GitHub – collaborative‑notepad – MIT licensed

Backend Stack

  • Node.js + Express
  • Socket.IO for real‑time communication
  • MongoDB for persistence
  • EJS as the template engine
  • Security: Helmet, rate limiting, CSP

Server Setup (Express + Socket.IO)

// server.js
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const mongoose = require('mongoose');

const app = express();
const server = http.createServer(app);
const io = socketIo(server);

// MongoDB connection
mongoose.connect(process.env.MONGODB_URI);

// Socket.IO connection handling
io.on('connection', (socket) => {
  console.log('User connected');

  socket.on('join-note', async (noteId) => {
    socket.join(noteId);
    // Broadcast user count
    io.to(noteId).emit('user-count',
      io.sockets.adapter.rooms.get(noteId)?.size || 0
    );
  });

  socket.on('note-update', async ({ noteId, content }) => {
    // Save to database
    await Note.findOneAndUpdate(
      { noteId },
      { content, lastModified: Date.now() }
    );
    // Broadcast to all users in room
    socket.to(noteId).emit('note-change', content);
  });
});

MongoDB Schema

// models/Note.js
const noteSchema = new mongoose.Schema({
  noteId: { type: String, unique: true, required: true },
  content: { type: String, default: '' },
  createdAt: { type: Date, default: Date.now },
  lastModified: { type: Date, default: Date.now },
  viewCount: { type: Number, default: 0 }
});

const Note = mongoose.model('Note', noteSchema);

Client‑Side Real‑Time Sync

// public/js/app.js
const socket = io();
const noteId = window.location.pathname.substring(1) || 'home';
const textarea = document.getElementById('note');
const userCountElement = document.getElementById('user-count');

// Join note room
socket.emit('join-note', noteId);

// Send updates to server (debounced)
let timeout;
textarea.addEventListener('input', () => {
  clearTimeout(timeout);
  timeout = setTimeout(() => {
    socket.emit('note-update', {
      noteId,
      content: textarea.value
    });
  }, 300);
});

// Receive updates from other users
socket.on('note-change', (content) => {
  if (document.activeElement !== textarea) {
    textarea.value = content;
  }
});

// Update user count
socket.on('user-count', (count) => {
  userCountElement.textContent = count;
});

Rate Limiting

// middleware/rateLimit.js
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per window
});

app.use('/api/', limiter);

Content Security Policy

// security/csp.js
const helmet = require('helmet');

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "'unsafe-inline'"],
    styleSrc: ["'self'", "'unsafe-inline'"]
  }
}));

XSS Prevention

const xss = require('xss');

// Example sanitization
const sanitizedContent = xss(userInput);

Express Routing for Custom URLs

app.get('/:noteId', async (req, res) => {
  const noteId = req.params.noteId;
  const note = await Note.findOne({ noteId }) ||
               await Note.create({ noteId });

  res.render('index', { note });
});

Auto‑Save Logic

let saveTimeout;
const AUTO_SAVE_DELAY = 3000; // 3 seconds

textarea.addEventListener('input', () => {
  clearTimeout(saveTimeout);
  saveTimeout = setTimeout(saveToDatabase, AUTO_SAVE_DELAY);
});

Theming (CSS Custom Properties)

:root {
  --bg-color: #fefae0;
  --text-color: #1e3a1f;
}

[data-theme="dark"] {
  --bg-color: #1e1e1e;
  --text-color: #e0e0e0;
}

Performance & Reliability Optimizations

  • Debouncing socket emissions to reduce server load
  • MongoDB indexing for fast note lookups
  • Connection pooling for efficient DB usage
  • Gzip compression middleware for faster asset delivery
  • Socket.IO rooms to isolate collaboration per note

Future Improvements (Roadmap)

  • Markdown support
  • Syntax highlighting for code blocks
  • Password‑protected notes
  • Export to PDF/TXT
  • Collaborative cursor positions
  • Voice notes
  • Mobile app

Tech Stack Summary

  • Node.js
  • Express
  • Socket.IO
  • MongoDB
  • EJS

Development Time

≈ 20 hours

License

MIT (open source)


If you found this helpful, give it a ❤️ and star the GitHub repository!

Back to Blog

Related posts

Read more »