Build With Abdallah logo Build With Abdallah Software · AI · Automation
Tutorial 6 min read Jun 07, 2026

Creating Real-Time Collaborative Applications with Next.js 16 and Yjs

In today's digital landscape, realtime collaboration has become a critical feature for many applications. Whether it's collaborative document editing, realtime data visualization,

A
Abdallah Mohamed
Senior Full-Stack Engineer
Creating Real-Time Collaborative Applications with Next.js 16 and Yjs

Creating Real-Time Collaborative Applications with Next.js 16 and Yjs

In today's digital landscape, real-time collaboration has become a critical feature for many applications. Whether it's collaborative document editing, real-time data visualization, or live chat applications, users expect seamless and instantaneous interaction. This tutorial will guide you through building a real-time collaborative application using Next.js 16 and Yjs. Next.js is a popular React framework that provides server-side rendering and static site generation, while Yjs is a powerful library for building real-time collaboration features.

Prerequisites

Before we start building our application, ensure you have the following installed on your machine:

  1. Node.js and npm: Next.js requires Node.js. You can download it from nodejs.org. Verify installation with:

    node -v
    npm -v
    
  2. Create Next App: This is a tool to quickly set up a new Next.js application. Install it globally using:

    npm install -g create-next-app
    
  3. Yjs: We will install this later within our project.

Project Structure

Let's set up the directory structure for our project. We'll start with creating a new Next.js application and then add the necessary files and directories for our collaborative features.

my-collab-app/
├── pages/
│   ├── api/
│   └── index.js
├── public/
├── styles/
│   └── globals.css
├── components/
├── package.json
└── README.md

Step 1: Setting Up the Next.js Project

First, create a new Next.js application using the create-next-app command:

npx create-next-app my-collab-app
cd my-collab-app

This command will generate the basic structure of a Next.js application. Navigate into the project directory to start working on it.

Step 2: Installing Yjs

Next, we need to add Yjs to our project. Yjs is a CRDT (Conflict-free Replicated Data Type) library that enables real-time collaboration.

Run the following command to install Yjs:

npm install yjs

Yjs provides the core functionality for real-time collaboration, such as shared data types and document synchronization.

Step 3: Creating a Basic Collaborative Document

Now, let's create a simple collaborative text editor using Yjs. We'll start by setting up a basic page in Next.js that includes a text area for users to edit collaboratively.

Create a new file collab.js in the pages directory:

// pages/collab.js
import { useEffect, useState } from 'react';
import * as Y from 'yjs';

export default function CollaborativeEditor() {
  const [editorContent, setEditorContent] = useState('');

  useEffect(() => {
    // Create a new Yjs document
    const ydoc = new Y.Doc();

    // Create a shared text type
    const yText = ydoc.getText('shared-text');

    // Observe changes to the shared text
    yText.observe(() => {
      setEditorContent(yText.toString());
    });

    // Set initial content
    yText.insert(0, 'Start collaborating...');

    return () => {
      ydoc.destroy();
    };
  }, []);

  const handleChange = (event) => {
    const ydoc = new Y.Doc();
    const yText = ydoc.getText('shared-text');
    yText.delete(0, yText.length);
    yText.insert(0, event.target.value);
  };

  return (
    <div>
      <h1>Collaborative Text Editor</h1>
      <textarea
        value={editorContent}
        onChange={handleChange}
        rows="10"
        cols="50"
      ></textarea>
    </div>
  );
}

Explanation

  • Y.Doc: This is the main document instance in Yjs, which holds all shared data types.
  • Y.Text: A shared text type that allows for collaborative text editing.
  • useEffect: We initialize the Yjs document and text type when the component mounts and set up an observer to update the local state whenever the shared text changes.
  • handleChange: This function updates the Yjs shared text whenever the user types in the textarea.

This setup creates a basic collaborative text editor, but it currently only works for a single user. We'll expand on this in the next steps to enable true multi-user collaboration.

Step 4: Enabling Multi-User Collaboration

To enable collaboration among multiple users, we must synchronize the Yjs document across clients. We'll use WebSockets for real-time communication between clients. For this, we need to set up a simple WebSocket server.

First, install the ws package to handle WebSocket connections:

npm install ws

Next, create a WebSocket server file in the root of your project:

// ws-server.js
const WebSocket = require('ws');
const Y = require('yjs');
const { WebsocketProvider } = require('y-websocket');

const wss = new WebSocket.Server({ port: 1234 });

wss.on('connection', (ws) => {
  const ydoc = new Y.Doc();
  const provider = new WebsocketProvider('ws://localhost:1234', 'collab-room', ydoc);

  ws.on('message', (message) => {
    const update = new Uint8Array(message);
    Y.applyUpdate(ydoc, update);
  });

  ydoc.on('update', (update) => {
    ws.send(update);
  });

  ws.on('close', () => {
    provider.destroy();
    ydoc.destroy();
  });
});

console.log('WebSocket server running on ws://localhost:1234');

Explanation

  • WebSocket Server: This server listens for connections and manages document updates.
  • Y.Doc: Each connection gets a Yjs document that syncs with the server.
  • WebsocketProvider: This links the server and Yjs document, allowing real-time updates.

Step 5: Connecting the Client to the WebSocket Server

Modify collab.js to connect to the WebSocket server and synchronize the Yjs document:

// pages/collab.js
import { useEffect, useState } from 'react';
import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';

export default function CollaborativeEditor() {
  const [editorContent, setEditorContent] = useState('');

  useEffect(() => {
    const ydoc = new Y.Doc();
    const provider = new WebsocketProvider('ws://localhost:1234', 'collab-room', ydoc);
    const yText = ydoc.getText('shared-text');

    yText.observe(() => {
      setEditorContent(yText.toString());
    });

    provider.on('status', (event) => {
      console.log(event.status); // Logs "connected" or "disconnected"
    });

    return () => {
      provider.destroy();
      ydoc.destroy();
    };
  }, []);

  const handleChange = (event) => {
    const ydoc = new Y.Doc();
    const yText = ydoc.getText('shared-text');
    yText.delete(0, yText.length);
    yText.insert(0, event.target.value);
  };

  return (
    <div>
      <h1>Collaborative Text Editor</h1>
      <textarea
        value={editorContent}
        onChange={handleChange}
        rows="10"
        cols="50"
      ></textarea>
    </div>
  );
}

Explanation

  • WebsocketProvider: Connects the client to the WebSocket server, enabling document synchronization.
  • Status Logging: Logs connection status to the console for debugging.

Complete Working Example

collab.js

// pages/collab.js
import { useEffect, useState } from 'react';
import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';

export default function CollaborativeEditor() {
  const [editorContent, setEditorContent] = useState('');

  useEffect(() => {
    const ydoc = new Y.Doc();
    const provider = new WebsocketProvider('ws://localhost:1234', 'collab-room', ydoc);
    const yText = ydoc.getText('shared-text');

    yText.observe(() => {
      setEditorContent(yText.toString());
    });

    provider.on('status', (event) => {
      console.log(event.status);
    });

    return () => {
      provider.destroy();
      ydoc.destroy();
    };
  }, []);

  const handleChange = (event) => {
    const ydoc = new Y.Doc();
    const yText = ydoc.getText('shared-text');
    yText.delete(0, yText.length);
    yText.insert(0, event.target.value);
  };

  return (
    <div>
      <h1>Collaborative Text Editor</h1>
      <textarea
        value={editorContent}
        onChange={handleChange}
        rows="10"
        cols="50"
      ></textarea>
    </div>
  );
}

ws-server.js

const WebSocket = require('ws');
const Y = require('yjs');
const { WebsocketProvider } = require('y-websocket');

const wss = new WebSocket.Server({ port: 1234 });

wss.on('connection', (ws) => {
  const ydoc = new Y.Doc();
  const provider = new WebsocketProvider('ws://localhost:1234', 'collab-room', ydoc);

  ws.on('message', (message) => {
    const update = new Uint8Array(message);
    Y.applyUpdate(ydoc, update);
  });

  ydoc.on('update', (update) => {
    ws.send(update);
  });

  ws.on('close', () => {
    provider.destroy();
    ydoc.destroy();
  });
});

console.log('WebSocket server running on ws://localhost:1234');

Common Errors and Fixes

  1. WebSocket Connection Error:

    • Error: "WebSocket connection to 'ws://localhost:1234' failed."
    • Fix: Ensure the WebSocket server is running and accessible on the specified port.
  2. Yjs Document Not Syncing:

    • Error: Changes in one client do not reflect in others.
    • Fix: Verify the WebsocketProvider is correctly set up in both server and client code.
  3. Module Not Found:

    • Error: "Cannot find module 'y-websocket'."
    • Fix: Install the y-websocket package using npm install y-websocket.

Conclusion

In this tutorial, we built a real-time collaborative text editor using Next.js and Yjs. By leveraging Yjs's CRDT capabilities and WebSockets for communication, we enabled multiple users to edit text simultaneously. This setup can be extended to other collaborative applications, enhancing user interaction and experience.

Sources