In this tutorial, we will guide you through adding real-time synchronization to a React Flow application using SuperViz. Real-time synchronization is a key feature for collaborative applications, allowing multiple users to interact with shared content simultaneously and see each other's changes as they happen. With React Flow and SuperViz, you can build interactive flowcharts that update live, providing a seamless collaborative experience.

We'll demonstrate how to integrate contextual comments and real-time mouse pointers into a React Flow application, enabling users to collaborate on flowcharts with real-time updates. This setup allows multiple participants to add nodes, create connections, and drag elements within the flowchart, with changes visible instantly to all users in the session. Let's get started!

Prerequisite

To follow this tutorial, you will need a SuperViz account and a developer token. If you already have an account and a developer token, you can move on to the next step.

Create an account

To create an account, go to https://dashboard.superviz.com/register and create an account using either Google or an email/password. It's important to note that when using an email/password, you will receive a confirmation link that you'll need to click to verify your account.

Retrieving a Developer Token

To use the SDK, you’ll need to provide a developer token, as this token is essential for associating SDK requests with your account. You can retrieve both development and production SuperViz tokens from the dashboard..
Copy and save the developer token, as you will need it in the next steps of this tutorial.

Step 1: Set Up Your React Application

To begin, you'll need to set up a new React project where we will integrate the React Flow and SuperViz packages for real-time collaboration.

1. Create a New React Project

First, create a new React application using Vite with TypeScript.

1
npm create vite@latest realtime-react-flow -- --template react-ts
2
cd realtime-react-flow

2. Install Required Libraries

Next, install the necessary libraries for our project:

1
npm install @superviz/room @superviz/collaboration @superviz/realtime reactflow uuid
  • @superviz/room: Core package for creating and managing SuperViz rooms.
  • @superviz/collaboration: Package containing components for collaboration features like comments, pins, and mouse pointers.
  • @superviz/realtime: SuperViz Real-Time library for integrating real-time synchronization into your application.
  • reactflow: A library for building interactive flowcharts and diagrams.
  • uuid: A library for generating unique identifiers, useful for creating unique participant IDs.

3. Configure tailwind

In this tutorial, we'll use the Tailwind css framework. First, install the tailwind package.

1
npm install -D tailwindcss postcss autoprefixer
2
npx tailwindcss init -p

We then need to configure the template path. Open tailwind.config.js in the root of the project and insert the following code.

1
/** @type {import('tailwindcss').Config} */
2
export default {
3
content: [
4
"./index.html",
5
"./src/**/*.{js,ts,jsx,tsx}",
6
],
7
theme: {
8
extend: {},
9
},
10
plugins: [],
11
}

Then we need to add the tailwind directives to the global CSS file. (src/index.css)

1
@tailwind base;
2
@tailwind components;
3
@tailwind utilities;

4. Set Up Environment Variables

Create a .env file in your project root and add your SuperViz developer key. This key will be used to authenticate your application with SuperViz services.

1
VITE_SUPERVIZ_API_KEY=YOUR_SUPERVIZ_DEVELOPER_KEY

Step 2: Implement the Main Application

In this step, we'll implement the main application logic to initialize SuperViz and handle real-time synchronization in a React Flow application.

1. Import Required Modules

Open src/App.tsx and add the necessary imports:

1
import { createRoom, Room } from "@superviz/room";
2
import { Comments, HTMLPin, MousePointers } from '@superviz/collaboration'
3
import { v4 as generateId } from "uuid";
4
5
import { useCallback, useEffect, useRef } from "react";
6
import { Realtime, type Channel } from "@superviz/realtime/client";
7
8
9
import ReactFlow, {
10
useNodesState,
11
Controls,
12
Background,
13
ConnectionLineType,
14
addEdge,
15
useEdgesState,
16
ConnectionMode,
17
Connection,
18
useViewport,
19
Node,
20
ReactFlowProvider,
21
} from "reactflow";
22
import "reactflow/dist/style.css";

Explanation:

  • SuperViz imports: We import core components from SuperViz packages for creating rooms and adding collaboration features.
  • ReactFlow imports: These provide the flowchart functionality and state management hooks.
  • React hooks: For managing state, effects, and callbacks.

2. Define Types and Initial Data

Define the Edge type and set up initial nodes and edges for the flowchart:

1
type Edge = {
2
type: ConnectionLineType;
3
animated: boolean;
4
source: string | null;
5
target: string | null;
6
sourceHandle: string | null;
7
targetHandle: string | null;
8
};
9
10
const initialNodes = [
11
{ id: "1", position: { x: 381, y: 265 }, data: { label: "Start" } },
12
{ id: "2", position: { x: 556, y: 335 }, data: { label: "Action" } },
13
{ id: "3", position: { x: 701, y: 220 }, data: { label: "Process" } },
14
{ id: "4", position: { x: 823, y: 333 }, data: { label: "End" } },
15
];
16
17
const initialEdges = [
18
{
19
id: "e1-2",
20
source: "1",
21
target: "2",
22
type: ConnectionLineType.SmoothStep,
23
animated: true,
24
},
25
{
26
id: "e2-3",
27
source: "2",
28
target: "3",
29
type: ConnectionLineType.SmoothStep,
30
animated: true,
31
},
32
{
33
id: "e3-4",
34
source: "3",
35
target: "4",
36
type: ConnectionLineType.SmoothStep,
37
animated: true,
38
},
39
];
40
41
42
const DEVELOPER_TOKEN = import.meta.env.VITE_SUPERVIZ_API_KEY;

Explanation:

  • Edge type: Defines the structure of edges in the flowchart.
  • initialNodes: Pre-defined set of nodes for the flowchart with positions and labels.
  • initialEdges: Pre-defined connections between nodes with animated smooth step lines.
  • DEVELOPER_TOKEN: Accesses the SuperViz API key from environment variables.

3. Create the Children Component

Implement the Children component which will contain the ReactFlow canvas and SuperViz integration:

1
const Children = () => {
2
const channelRef = useRef<Channel | null>(null);
3
const roomRef = useRef<Room | null>(null);
4
const initializedRef = useRef(false);
5
const commentsRef = useRef<Comments | null>(null);
6
const mousePointersRef = useRef<MousePointers | null>(null);
7
8
const { x, y, zoom } = useViewport();
9
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
10
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

Explanation:

  • Refs: Used to store instances of SuperViz components and track initialization state.
  • useViewport: Provides the current viewport state (position and zoom).
  • useNodesState & useEdgesState: Hooks from ReactFlow to manage nodes and edges states.

4. Initialize SuperViz Integration

Create the initialization function to set up SuperViz components and realtime synchronization:

1
const initialize = useCallback(async () => {
2
if (initializedRef.current) return;
3
4
initializedRef.current = true;
5
6
try {
7
const participantId = generateId();
8
const realtime = new Realtime(DEVELOPER_TOKEN, {
9
participant: {
10
id: participantId
11
},
12
});
13
14
channelRef.current = await realtime.connect("react-flow-nodes-sync");
15
channelRef.current!.subscribe<{ edge: Edge }>(
16
"new-edge",
17
({ data, participantId: senderId }) => {
18
if (senderId === participantId) return;
19
20
setEdges((eds) => addEdge(data.edge, eds));
21
}
22
);
23
24
channelRef.current!.subscribe<{ node: Node }>(
25
"node-drag",
26
({ data, participantId: senderId }) => {
27
if (senderId === participantId) return;
28
29
setNodes((nds) =>
30
nds.map((node) =>
31
node.id === data.node.id ? { ...node, ...data.node } : node
32
)
33
);
34
}
35
);
36
37
const room = await createRoom({
38
developerToken: DEVELOPER_TOKEN,
39
roomId: "ROOM_ID",
40
participant: {
41
id: participantId,
42
name: "Participant",
43
},
44
group: {
45
id: "GROUP_ID",
46
name: "GROUP_NAME",
47
},
48
});
49
50
mousePointersRef.current = new MousePointers('react-flow-container')
51
const pin = new HTMLPin('react-flow-container', {
52
dataAttributeName: "data-id",
53
dataAttributeValueFilters: [/.*null-(target|source)$/],
54
})
55
56
commentsRef.current = new Comments(pin, {
57
buttonLocation: 'comments'
58
})
59
60
room.addComponent(commentsRef.current)
61
room.addComponent(mousePointersRef.current)
62
63
roomRef.current = room;
64
} catch (error) {
65
console.error("Error initializing SuperViz Room:", error);
66
}
67
}, []);

Explanation:

  • Realtime setup: Creates a Realtime instance for synchronizing data between participants.
  • Channel subscriptions: Sets up listeners for edge and node changes from other participants.
  • createRoom: Initializes a SuperViz room with participant and group information.
  • MousePointers: Creates a component to show participants' cursor positions in real-time.
  • HTMLPin & Comments: Sets up components for contextual comments with proper pinning to HTML elements.

5. Implement Event Handlers

Define handlers for flowchart interactions like drag events and connections:

1
const onDragOver = useCallback(
2
(event: React.DragEvent<HTMLButtonElement | HTMLDivElement>) => {
3
event.preventDefault();
4
event.dataTransfer.dropEffect = "move";
5
},
6
[]
7
);
8
9
const onNodeDrag = useCallback(
10
(_: React.MouseEvent, node: Node) => {
11
channelRef.current?.publish("node-drag", { node });
12
},
13
[channelRef]
14
);
15
16
const onConnect = useCallback(
17
(connection: Connection) => {
18
const edge: Edge = {
19
...connection,
20
type: ConnectionLineType.SmoothStep,
21
animated: true,
22
};
23
24
setEdges((eds) => addEdge(edge, eds));
25
26
channelRef.current!.publish("new-edge", {
27
edge,
28
});
29
},
30
[setEdges]
31
);

Explanation:

  • onDragOver: Handles drag operations in the flowchart.
  • onNodeDrag: Publishes node position updates when a user drags a node, allowing other participants to see the changes.
  • onConnect: Creates and publishes new edge connections when a user connects nodes.

6. Set Up Effect Hooks

Add effect hooks to handle viewport changes and component initialization/cleanup:

1
useEffect(() => {
2
if(!mousePointersRef.current) return;
3
4
mousePointersRef.current.transform({
5
translate: {
6
x: x,
7
y: y,
8
},
9
scale: zoom,
10
});
11
}, [x, y, zoom]);
12
13
useEffect(() => {
14
initialize();
15
16
return () => {
17
if(roomRef.current) {
18
roomRef.current.leave();
19
}
20
21
if(channelRef.current) {
22
channelRef.current.disconnect()
23
}
24
}
25
}, []);

Explanation:

  • First useEffect: Updates the mouse pointers transform when the ReactFlow viewport changes.
  • Second useEffect: Initializes SuperViz components on mount and handles cleanup on unmount.

7. Render the Component

Add the JSX to render the ReactFlow canvas and attach SuperViz components:

1
return (
2
<div className="w-full h-full bg-gray-200 flex items-center justify-center flex-col">
3
<header className="w-full p-5 bg-purple-400 flex items-center justify-between">
4
<h1 className="text-white text-2xl font-bold">React Flow + SuperViz</h1>
5
<div id="comments" className="flex gap-2"></div>
6
</header>
7
<main className="flex-1 w-full h-full">
8
<div id="react-flow-container" className="w-full h-full">
9
<ReactFlow
10
nodes={nodes}
11
onConnect={onConnect}
12
onDragOver={onDragOver}
13
onNodeDrag={onNodeDrag}
14
edges={edges}
15
onNodesChange={onNodesChange}
16
onEdgesChange={onEdgesChange}
17
connectionMode={ConnectionMode.Loose}
18
>
19
<Controls showFitView={false} />
20
<Background />
21
</ReactFlow>
22
</div>
23
</main>
24
</div>
25
);
26
};

Explanation:

  • Header: Contains the title and a container for comments.
  • ReactFlow: The main flowchart component with event handlers for node changes, edge connections, and drag operations.
  • react-flow-container: The ID used by SuperViz components to attach to the correct DOM element.

8. Create the App Component

Finally, create the main App component that wraps the Children component with ReactFlowProvider:

1
const App = () => {
2
return (
3
<ReactFlowProvider>
4
<Children />
5
</ReactFlowProvider>
6
)
7
}
8
9
export default App;

Explanation:

  • ReactFlowProvider: Provides the necessary context for ReactFlow components.
  • Children: Our component that contains the ReactFlow canvas and SuperViz integration.

Step 3: Understanding the Integration

Let's examine how the different parts of the application work together to provide a collaborative flowchart experience:

  1. Real-time Synchronization
    • The Realtime component from SuperViz enables real-time communication between participants.
    • When a user drags a node or creates a connection, the changes are published to all participants.
    • Each participant subscribes to these events and updates their local state accordingly.
  2. SuperViz Room
    • The Room created with createRoom provides the foundation for collaboration features.
    • It manages participant information and provides the context for collaboration components.
  3. Collaboration Features
    • MousePointers show the cursor positions of all participants in real-time.
    • HTMLPin enables pinning comments to specific elements in the flowchart.
    • Comments allow participants to add contextual feedback to specific parts of the diagram.
  4. ReactFlow Integration
    • ReactFlow provides the interactive flowchart capabilities.
    • Its state management hooks (useNodesState, useEdgesState) are integrated with SuperViz's real-time features.
    • The viewport state is synchronized with SuperViz components to ensure proper positioning of mouse pointers and comments.

Step 4: Running the Application

1. Start the React Application

To run your application, use the following command in your project directory:

1
npm run dev

This command will start the development server and open your application in the default web browser. You can interact with the flowchart and see updates in real-time across multiple participants.

2. Test the Application

  • Collaborative Flowchart: Open the application in multiple browser windows or tabs to simulate multiple participants and verify that changes made by one participant are reflected in real-time for others.
  • Mouse Pointers: Observe how each participant's cursor is visible to others as they move around the diagram.
  • Comments: Test adding comments to specific elements in the flowchart and verify they appear for all participants.
  • Real-Time Updates: Test dragging nodes and creating connections to ensure changes synchronize across sessions.

Summary

In this tutorial, we built a collaborative flowchart application using React Flow and SuperViz. We implemented real-time synchronization of flowchart elements, enabling multiple users to collaborate seamlessly on a shared diagram. We added mouse pointers to show participant cursor positions and integrated contextual comments to allow for feedback on specific parts of the diagram.

This setup can be extended and customized to fit various scenarios where real-time collaboration and workflow visualization are required, such as team planning, system architecture design, or process mapping.

Feel free to explore the full code and further examples in the GitHub repository for more details.