Collaboration In Real Time With React And Node
Published: 2023-10-18 13:37:06

by [King AJ](https://blog.openreplay.com/authors/king-aj)

<blockquote><em>
In today's rapidly evolving digital environment, real-time collaboration has
become a paramount feature in various web applications. The ability to work together seamlessly, irrespective of geographical boundaries, has transformed how teams collaborate and communicate. This article will show how to build a web app providing a real time collaboration board using React and Node.
</em></blockquote>

<div style="background-color:#efefef; border-radius:8px; padding:10px; display:block;">
<hr/>
<h3><em>Session Replay for Developers</em></h3> 
<p><em>Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data.</em></p>
<img alt="OpenReplay" style="margin-top:5px; margin-bottom:5px;" width="768" height="400" src="https://raw.githubusercontent.com/openreplay/openreplay/main/static/openreplay-git-hero.svg" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<p><em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em><p>
<hr/>
</div>

Real-time collaboration involves multiple users interacting dynamically and instantly while collaborating on a shared task or project. Unlike older collaboration methods with communication delays and updates, real-time collaboration lets team members contribute, edit, and see changes in the moment. This instant syncing promotes a feeling of togetherness and speeds up decision-making, making it a vital tool for contemporary workflows.
Incorporating real-time collaboration features brings a host of benefits:
* Global Reach: Real-time collaboration allows remote teams to collaborate as if they were in the same physical location. This feature is particularly valuable in an era where remote work has become the norm, enabling teams spread across the globe to collaborate seamlessly.
* Brainstorm Together: Real-time collaboration sparks group brainstorming and idea-sharing, letting everyone collectively tackle challenges and create fresh solutions.
* Efficiency Boost: Real-time collaboration eliminates communication delays, letting team members engage and contribute on the spot. This speeds up solving problems and streamlines workflows.
There are different forms of real-time collaboration, such as:
Document Collaboration, Video Conferencing, Instant Messaging and Chat, Project Management Tools, Co-browsing, Shared Calendars, Interactive Presentations, Social Media Collaboration, and Multiplayer Gaming. For this article, we'll be focusing on Real-time Whiteboarding.
A Real-time whiteboard is a dynamic digital tool replicating a traditional whiteboard's functionality in an online environment. It provides individuals and teams with a shared canvas where they can collaborate in real-time, allowing for the creation, manipulation, and visualization of ideas, concepts, and information.
## Our project
Using [`React`](https://react.dev/) and [`Node.js`](https://nodejs.org/en), we will delve into the exciting realm of real-time collaboration by building a real-time collaborating board using `React` and `Node.js`. Our project will enable users to work on a shared virtual board in real time, instantly updating the content and changes for all participants. We will incorporate the drag-and-drop feature so users can effortlessly move and arrange elements on the board, making collaboration even more intuitive and engaging.
Whether you want to build a collaborative tool for remote teams, an educational platform, a project management application, or any other project demanding real-time collaboration, this article will equip you with the essential skills and knowledge to develop interactive and efficient real-time applications. So, let’s dive in and unlock the immense potential of real-time collaboration with `React` and `Node.js.`
To set up our React app for this project, we'll do the following:
* Create React Application: Navigate to your desired directory, open your terminal, and run the following command to create a new React application using `create-react-app`:
```bash!
npx create-react-app collaborate_client
```
* Navigating into the Project Directory: To move into the newly created project directory;
```bash!
cd collaborate_client
```
### Installing Dependencies
With our project initialized, it's time to install the necessary dependencies that will power our real-time collaborating board. These dependencies include [`socket.io`](https://`socket.io`/) for real-time communication and [`RoughJS`](https://roughjs.com/) for drawing capabilities.
* **`socket.io`**: Install the `socket.io` library to establish WebSocket connections for real-time data exchange;
```bash!
npm install `socket.io`
```
* **RoughJS**: Integrate the rough.js library to enable drawing functionality on the collaborating board;
```bash
npm install --save roughjs
```
## Creating the Collaborating Board User Interface with React
The' Canvas' component lies the heart of our real-time collaborating board. The [`Canvas`](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) is an HTML element that serves as a blank slate where we can paint, draw, and manipulate graphical elements using JavaScript.
For the user interface, we'll create a `WhiteBoard` component where users can manipulate graphical elements in our `React` application.
### Building a Canvas Component using React
Before delving into [`RoughJS`](https://roughjs.com/) and drawing capabilities, let's set the stage by creating our `WhiteBoard` component. In your `React` project, navigate to the appropriate directory and create a new file named Whiteboard.js. Inside this file, define your `WhiteBoard` component:
```javascript
import React, { useLayoutEffect } from "react";
const WhiteBoard = () => {
 useLayoutEffect(() => {
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");
  // Set canvas dimensions and initial styles
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  ctx.strokeStyle = "black";
  ctx.lineWidth = 5;
  // Implement drawing functionality here
 }, []);
 //implement event listeners for drawing interactions
 return (
  <>
   <canvas
    id="canvas"
    width={window.innerWidth}
    height={window.innerHeight}
   ></canvas>
  </>
 );
};
export default WhiteBoard;
```
In the above code, we import the necessary dependencies, create a `WhiteBoard` functional component, and utilize the `useLayoutEffect` hooks provided by `React`. Within the `useLayoutEffect` hook, we access the `canvas` element and its 2D rendering context to configure its dimensions and initial styles.
### Integrating RoughJS within the Canvas Component
`RoughJS` is a lightweight library that enables us to create hand-drawn, sketch-like graphics within our canvas. By integrating `RoughJS`, we can transform our plain whiteboard into a playground of creativity, where lines, shapes, and textures come to life with an organic, handcrafted feel.
To augment our `WhiteBoard` component with the `RoughJS` within the component file, update the code as follows:
```javascript
import React, { useLayoutEffect } from "react";
import rough from "roughjs";
const WhiteBoard = () => {
 useLayoutEffect(() => {
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");
  // Initialize RoughJS instance
  const roughCanvas = rough.canvas(canvas);
  // Set canvas initial styles
  ctx.strokeStyle = "black";
  ctx.lineWidth = 5;
  // Add event listeners for drawing interactions
  // Implement drawing functionality here
 }, []);
 return (
  <>
   <canvas
    id="canvas"
    width={window.innerWidth}
    height={window.innerHeight}
   ></canvas>
  </>
 );
};
export default WhiteBoard;
```
In the updated code, we've imported the `RoughJS` library and created an instance using the `rough.canvas()` method, associating it with our `canvas` element. This instance, stored in the `roughCanvas`, will allow us to apply `RoughJS` primitives and effects with which we can draw on the whiteboard.
With `RoughJS,` there are endless possibilities for shapes, lines, and shades we can draw. For this article, we'll look at how to draw lines and rectangles on our Whiteboard. You can build on this knowledge and add other shapes and features [RoughJS](https://roughjs.com/) supports.
### Drawing lines on our Canvas
To draw lines on our `canvas` using `RoughJS` we'll do the following;
* **Initialize Drawing State**: Begin by setting up the necessary state to track drawing interactions:
```javascript
const [drawing, setDrawing] = useState(false);
const [elements, setElements] = useState([]);
```
* **Handle Mouse Down Event**: When a user initiates drawing by pressing the mouse button, we'll set the `drawing` state to true and create a new element. In the `handleMouseDown` function, we're utilizing the initial `clientX` and `clientY` values to mark the starting point of our drawing. When the user clicks the mouse, we want to record where that click occurred, as it will be the start of the line they're about to draw:
```javascript
const handleMouseDown = (e) => {
 setDrawing(true);
 const { clientX, clientY } = e;
 // Create a new line element with the same start and end points
 const element = createElement(clientX, clientY, clientX, clientY);
 setElements((prevState) => [...prevState, element]);
};
```
* **Handle Mouse Move Event**: With the mouse button still down, we continuously update the element we created in `handleMouseDown` with the current path of the mouse as the user moves the mouse on the `canvas`:
```javascript
const handleMouseMove = (e) => {
 if (!drawing) return;
 const { clientX, clientY } = e;
 const index = elements.length - 1;
 const { x1, y1 } = elements[index];
 // Update the end point of the element and create an element
 const updatedElement = createElement(x1, y1, clientX, clientY);
 const elementsCopy = [...elements];
 elementsCopy[index] = updatedElement;
 setElements(elementsCopy);
};
```
* **Creating our line**: The mouse coordinates will be sent to the `createElement` function, which then utilizes the `RoughJS` library to generate a hand-drawn representation of the element. This function then returns the coordinates and the `RoughJS` element, which will be stored in our `elements` state:
```javascript
const createElement = (x1, y1, x2, y2) => {
 // Use the RoughJS generator to create a rough element (line or rectangle)
 const roughElement = generator.line(x1, y1, x2, y2);
 // Return an object representing the element, including its coordinates and RoughJS representation
 return { x1, y1, x2, y2, roughElement };
};
```
* **Rendering our elements**: Using our `useLayoutEffect` function with every update to the `elements` state we render the elements stored in the `state`.
```javascript
useLayoutEffect(() => {
 // Get the canvas element by its ID
 const canvas = document.getElementById("canvas");
 // Get the 2D rendering context of the canvas
 const ctx = canvas.getContext("2d");
 // Create a RoughJS canvas instance associated with the canvas element
 const roughCanvas = rough.canvas(canvas);
 // Set stroke style and line width for the canvas context
 ctx.strokeStyle = "black";
 ctx.lineWidth = 5;
 // Clear the entire canvas to ensure a clean drawing surface
 ctx.clearRect(0, 0, canvas.width, canvas.height);
 // If there are saved elements to render
 if (elements && elements.length > 0) {
  // Iterate through each saved element
  elements.forEach(({ roughElement }) => {
   // Use RoughJS to draw the element on the canvas
   roughCanvas.draw(roughElement);
  });
 }
}, [elements]); // This effect depends on the 'elements' state; re-run when it changes
```
* **Handle Mouse Up Event**: When the user releases the mouse button, we set the `drawing` state to false, halting the drawing process;
```javascript
const handleMouseUp = (e) => {
 setDrawing(false);
};
```
By implementing these steps, users can draw lines by clicking and dragging the mouse cursor on the `canvas`. Here's the `WhiteBoard` component with the ability to draw lines on our `canvas`;
```javascript
import React, { useState, useLayoutEffect } from "react";
import rough from "roughjs/bundled/rough.esm.js";
// Create a RoughJS generator instance
const generator = rough.generator();
const WhiteBoard = () => {
 // State for managing drawing elements and interactions
 const [elements, setElements] = useState([]);
 const [drawing, setDrawing] = useState(false);
 // UseLayoutEffect: Responsible for rendering drawing elements
 useLayoutEffect(() => {
  // Get the canvas element by its ID
  const canvas = document.getElementById("canvas");
  // Get the 2D rendering context of the canvas
  const ctx = canvas.getContext("2d");
  // Create a RoughJS canvas instance associated with the canvas element
  const roughCanvas = rough.canvas(canvas);
  // Set stroke style and line width for the canvas context
  ctx.strokeStyle = "black";
  ctx.lineWidth = 5;
  // Clear the entire canvas to ensure a clean drawing surface
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  // If there are saved elements to render
  if (elements && elements.length > 0) {
   // Iterate through each saved element
   elements.forEach(({ roughElement }) => {
    // Use RoughJS to draw the element on the canvas
    roughCanvas.draw(roughElement);
   });
  }
 }, [elements]);
 // Function to create a new drawing element
 const createElement = (x1, y1, x2, y2) => {
  // Use the RoughJS generator to create a rough element (line or rectangle)
  const roughElement = generator.line(x1, y1, x2, y2);
  // Return an object representing the element, including its coordinates and RoughJS representation
  return { x1, y1, x2, y2, roughElement };
 };
 // Event handler for mouse down
 const handleMouseDown = (e) => {
  setDrawing(true);
  const { clientX, clientY } = e;
  // Create a new drawing element when mouse down is detected
  const element = createElement(clientX, clientY, clientX, clientY);
  setElements((prevState) => [...prevState, element]);
 };
 // Event handler for mouse move
 const handleMouseMove = (e) => {
  if (!drawing) return;
  const { clientX, clientY } = e;
  // Find the index of the last element created during mouse down
  const index = elements.length - 1;
  const { x1, y1 } = elements[index];
  // Update the element's coordinates for dynamic drawing
  const UpdatedElement = createElement(x1, y1, clientX, clientY);
  const elementsCopy = [...elements];
  elementsCopy[index] = UpdatedElement;
  setElements(elementsCopy);
 };
 // Event handler for mouse up
 const handleMouseUp = () => {
  setDrawing(false);
 };
 // Return JSX to render the collaborative canvas
 return (
  <>
   <canvas
    id="canvas"
    onMouseDown={handleMouseDown}
    onMouseUp={handleMouseUp}
    onMouseMove={handleMouseMove}
    width={window.innerWidth}
    height={window.innerHeight}
   ></canvas>
  </>
 );
};
export default WhiteBoard;
```
Let's test our application:
![-](https://blog.openreplay.com/images/collaboration-in-real-time-with-react-and-node/images/image1.gif)
The video above shows that our code works and draws lines on our whiteboard using our mouse coordinates.
### Drawing Rectangles lines on our Canvas
The process for drawing rectangles on our whiteboard is almost the same as creating lines but only changes when we get to the `createElement` function. Before we see the update to our `createElement` function, let's create a state that stores the current tool the user intends to utilize.
```javascript
const [tool, setTool] = useState('line');
```
By default, the tool is set to line on our canvas. Now we can update our `createElement` function to accommodate rectangles.
```javascript
const createElement = (x1, y1, x2, y2) => {
 let roughElement;
 // Use the RoughJS generator to create a rough element (line or rectangle)
 if (tool === "line") {
  roughElement = generator.line(x1, y1, x2, y2);
 } else if (tool === "rect") {
  roughElement = generator.rectangle(x1, y1, x2 - x1, y2 - y1);
 }
 // Return an object representing the element, including its coordinates and RoughJS representation
 return { x1, y1, x2, y2, roughElement };
};
```
Now, we need to add buttons to allow the users to select what tool they want to use on our canvas.
```javascript
return (
 <>
  <div className="d-flex col-md-2 justify-content-center gap-1">
   <div className="d-flex gap-1 align-items-center">
    <label htmlFor="line">Line</label>
    <input
     type="radio"
     id="line"
     name="tool"
     value="line"
     checked={tool === "line"}
     className="mt-1"
     onChange={(e) => setTool(e.target.value)}
    />
   </div>
   <div className="d-flex gap-1 align-items-center">
    <label htmlFor="rect">Rectangle</label>
    <input
     type="radio"
     name="tool"
     id="rect"
     checked={tool === "rect"}
     value="rect"
     className="mt-1"
     onChange={(e) => setTool(e.target.value)}
    />
   </div>
  </div>
  <canvas
   id="canvas"
   onMouseDown={handleMouseDown}
   onMouseUp={handleMouseUp}
   onMouseMove={handleMouseMove}
   width={window.innerWidth}
   height={window.innerHeight}
  ></canvas>
 </>
);
```
Now, let's test our application:
![-](https://blog.openreplay.com/images/collaboration-in-real-time-with-react-and-node/images/image2.gif)
From the video above, we can see that when we select rectangles, we can draw rectangles on our whiteboard based on the mouse coordinates.
 
<CTA_Middle_Programming />
 
## Enhancing Interactivity: Enabling Drag and Drop
To offer users the ability to drag and drop elements on the canvas, we'll do the following:
* **Introducing Selection Tool**: We’ll provide users with a selection tool indicated by a radio button. This tool will allow users to interact with and move existing elements when the button is selected.
```javascript
<input
  type="radio"
  id="selection"
  checked={tool === "selection"}
  onChange={() => setTool("selection")}
  />
<label htmlFor="selection">Drag n Drop</label>
```
* **Detecting Cursor Over an Element**: To ascertain if the cursor is over an element, we'll implement a function named `getElementAtPosition`. This function will determine if the cursor is within the boundaries of any existing `elements` while the mouse is down.
```javascript
const distance = (a, b) =>
 Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
const getElementAtPosition = (x, y) => {
 // Iterate through each element in the 'elements' array
 return elements.find((element) => {
  const { elementType, x1, y1, x2, y2 } = element;
  // Depending on the element type (line or rectangle), perform checks
  if (elementType === "rect") {
   // Check if the cursor position (x, y) falls within the boundaries of the rectangle
   const minX = Math.min(x1, x2);
   const maxX = Math.max(x1, x2);
   const minY = Math.min(y1, y2);
   const maxY = Math.max(y1, y2);
   return x >= minX && x <= maxX && y >= minY && y <= maxY;
  } else {
   // Check if the cursor is close enough to the line using a mathematical offset
   const a = { x: x1, y: y1 };
   const b = { x: x2, y: y2 };
   const c = { x, y };
   const offset = distance(a, b) - (distance(a, c) + distance(b, c));
   return Math.abs(offset) < 1;
  }
 });
};
```
The `getElementAtPosition` function takes in the current cursor coordinates (x and y) as parameters. We then use the `.find()` method to iterate through the elements array, which contains all the drawing elements on the canvas.
We retrieve the `elementType` and its current coordinates for each element in the array. If the element is a rectangle, we calculate the minimum and maximum `x` and `y` values to define the rectangle's boundaries.
We then check if the cursor's `x` coordinate falls within the range of the rectangle's `x` boundaries and if the cursor's `y` coordinate falls within the range of the rectangle's `y` boundaries. If both conditions are true, the cursor is positioned over the rectangle, so our function returns true.
If the element is a line, we calculate the distance between the cursor's coordinates and the line segment defined by the element's `x1`, `y1`, `x2`, and `y2` properties.
We then compare the calculated offset to a small threshold value (in this case, 1). If the absolute value of the offset is less than the threshold, the cursor is considered to be positioned near the line segment, so our function returns true.
The function returns false if the cursor is not positioned over any existing element.
* **Storing the Element to Drag**: When the user presses the mouse down while the selection tool is active and the cursor is over an element, we'll store that element in a state, along with the initial offset between the cursor and the top-left corner of the element.
```javascript
const handleMouseDown = (e) => {
 const { clientX, clientY } = e;
 // Check if the current tool is "Selection"
 if (tool === "selection") {
  // Find the element at the clicked position
  const element = getElementAtPosition(clientX, clientY);
  // If an element is found
  if (element) {
   // Calculate the offset from the top-left corner of the element
   const offsetX = clientX - element.x1;
   const offsetY = clientY - element.y1;
   // Store the selected element along with the offset
   setSelectedElement({ ...element, offsetX, offsetY });
   // Set the action to "moving" to indicate that dragging is in progress
   setAction("moving");
  }
 } else {
  // If the tool is not "Selection", execute code for drawing
  // ... (code for drawing)
 }
};
```
* **Updating Element Coordinates**: In the `handleMouseMove` function, when the user is in the "moving" state (i.e., dragging an element), we calculate the new position of the element based on the mouse cursor's position and the initial offset. We then update the element's coordinates using the `updateElement` function:
```javascript
const handleMouseMove = (e) => {
 const { clientX, clientY } = e;
 // Check if the current tool is "Selection"
 if (tool === "selection") {
  // Determine cursor style based on whether the mouse is over an element
  e.target.style.cursor = getElementAtPosition(clientX, clientY, elements)
   ? "move"
   : "default";
 }
 // Check the current action
 if (action === "drawing") {
  // ... (code for drawing)
 } else if (action === "moving") {
  // If in "moving" action (dragging an element)
  const { id, x1, x2, y1, y2, elementType, offsetX, offsetY } =
   selectedElement;
  const width = x2 - x1;
  const height = y2 - y1;
  // Calculate the new position of the dragged element
  const newX = clientX - offsetX;
  const newY = clientY - offsetY;
  // Update the element's coordinates to perform dragging
  const UpdatedElement = createElement(
   id,
   newX,
   newY,
   newX + width,
   newY + height,
   elementType,
  );
  const elementsCopy = [...elements];
  elementsCopy[id] = UpdatedElement;
  setElements(elementsCopy);
 }
};
```
Following these steps, we have equipped our canvas with a dynamic drag-and-drop capability. Users can now easily interact with existing elements, moving them across the canvas.
![-](https://blog.openreplay.com/images/collaboration-in-real-time-with-react-and-node/images/image3.gif)

## Creating the Real-Time Communication Server with Node.js
A powerful collaboration experience requires a server that seamlessly handles real-time communication between users. To set up our server, we'll do the following:
### Install Required Dependencies
Before diving into the Server setup, we must ensure we have the tools in our toolkit. Use the following command to install the required dependencies on our Server:
```bash!
npm install express cors socket.io
```
* **`Express`**: A popular and flexible Node.js framework that simplifies the process of building robust web applications and APIs. It provides middleware and routing capabilities, making it ideal for creating server-side applications.
* **`CORS` (Cross-Origin Resource Sharing)**: A middleware package that enables Cross-Origin requests. In our case, we'll use it to ensure that our client application (running on a different origin) can interact with the server.
* **`Socket.io`**: A real-time communication library that facilitates bidirectional communication between clients and the Server. It works over WebSocket connections but also gracefully degrades to other transport mechanisms when necessary.

### Configuring Express and Importing Dependencies:

To begin the Server setup, create a file named server.js (or your chosen filename).
```bash!
touch server.js
```
We'll then import the dependencies and set up the configuration for `Express`:
```javascript
const express = require('express');
const cors = require('cors');
const app = express();
const PORT = process.env.PORT || 5000;
```
### Configure CORS and Start Server:
To ensure proper cross-origin communication, we'll configure CORS settings and start the Server:
```javascript
const { createServer } = require("http");
const { Server } = require("socket.io");
const httpServer = createServer();
const io = new Server(httpServer, {
 cors: {
  origin: "http://localhost:3000", // Specify your front-end origin
  AccessControlAllowOrigin: "http://localhost:3000",
  allowedHeaders: ["Access-Control-Allow-Origin"],
  credentials: true,
 },
});
httpServer.listen(PORT, () => {
 console.log(`Server is listening on port ${PORT}`);
});
```
In this setup, we've created an `Express` app and set up the `CORS` configuration to allow communication between our client (running on port 3000) and Server. The `socket.io` library is integrated into the `httpServer` instance, enabling real-time communication.
## Implementing Real-Time Communication.
To implement real-time collaboration between our users we'll have to configure our client (React-app) to connect with our Server by updating our Canvas component with the code below:
```javascript
const [socket, setSocket] = useState(null);
// useEffect hook to establish and manage the socket connection
useEffect(() => {
 // Define the server URL
 const server = "http://localhost:5000";
 // Configuration options for the socket connection
 const connectionOptions = {
  "force new connection": true,
  reconnectionAttempts: "Infinity",
  timeout: 10000,
  transports: ["websocket"],
 };
 // Establish a new socket connection
 const newSocket = io(server, connectionOptions);
 setSocket(newSocket);
 // Event listener for successful connection
 newSocket.on("connect", () => {
  console.log("Connected to newSocket.io server!");
 });
 // Event listener for receiving served elements from the server
 newSocket.on("servedElements", (elementsCopy) => {
  setElements(elementsCopy.elements);
 });
 // Clean up the socket connection when the component is unmounted
 return () => {
  newSocket.disconnect();
 };
}, []); // Empty dependency array ensures the effect runs only once, during component mount
```
We will then leverage the event-driven architecture of `socket.io`, employing its `on` and `emit` mechanisms to facilitate seamless data transmission between the client and the server. On the client side, we will enhance the `updateElement` function to include a process whereby, with each element update, the data is transmitted to the server.
```javascript
const updateElement = (id, x1, y1, x2, y2, tool) => {
 const UpdatedElement = createElement(id, x1, y1, x2, y2, tool);
 const elementsCopy = [...elements];
 elementsCopy[id] = UpdatedElement;
 setElements(elementsCopy);
 socket.emit("elements", elementsCopy);
};
```
Subsequently, our server will send the received data to other connected clients within the network. This ensures real-time synchronization and collaboration among all participants.
```javascript
let connections = [];
let elements;
socket.on("elements", (data) => {
 elements = data;
 connections.forEach((con) => {
  if (con.id !== socket.id) {
   con.emit("servedElements", { elements });
  }
 });
});
```
When the data gets to other clients, we'll then update the `elements` state received, causing the `useLayoutEffect` to re-render, thus drawing the updated element on the canvas
```javascript
new socket.on("servedElements", (elementsCopy) => {
 setElements(elementsCopy.elements);
});
```
With this done, whenever one client makes an update, all other clients connected to our Server receive the update. Now, let's test our application:
![-](https://blog.openreplay.com/images/collaboration-in-real-time-with-react-and-node/images/image4.gif)
From the video above, we can see that once one client begins drawing, the other client receives the update and can add to the drawing, allowing for real-time collaboration by all clients on the network.

## Conclusion

In this article, we've embarked on an exciting journey of creating a real-time collaboration board powered by the dynamic duo of `React.js` and `Node.js`, alongside the potent tools of `socket.io` and `RoughJS`. We've also delved into the realm of seamless teamwork, focusing on sketching lines and rectangles and implementing the drag-and-drop feature on the canvas. Additionally, more shapes and features can be integrated into this project. Beyond what we've already explored, `RoughJS` offers a treasure trove of inspiration to enhance your creations. Armed with `React.js`, `Node.js`, and the insights gained here, you can infuse your projects with the magic of real-time collaboration.
### Resources
[Github repo](https://github.com/King-AJr/collaborative_board)