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.
1npm create vite@latest realtime-react-flow -- --template react-ts2cd realtime-react-flow
2. Install Required Libraries
Next, install the necessary libraries for our project:
1npm 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.
1npm install -D tailwindcss postcss autoprefixer2npx 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} */2export default {3content: [4"./index.html",5"./src/**/*.{js,ts,jsx,tsx}",6],7theme: {8extend: {},9},10plugins: [],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.
1VITE_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:
1import { createRoom, Room } from "@superviz/room";2import { Comments, HTMLPin, MousePointers } from '@superviz/collaboration'3import { v4 as generateId } from "uuid";45import { useCallback, useEffect, useRef } from "react";6import { Realtime, type Channel } from "@superviz/realtime/client";789import ReactFlow, {10useNodesState,11Controls,12Background,13ConnectionLineType,14addEdge,15useEdgesState,16ConnectionMode,17Connection,18useViewport,19Node,20ReactFlowProvider,21} from "reactflow";22import "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:
1type Edge = {2type: ConnectionLineType;3animated: boolean;4source: string | null;5target: string | null;6sourceHandle: string | null;7targetHandle: string | null;8};910const 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];1617const initialEdges = [18{19id: "e1-2",20source: "1",21target: "2",22type: ConnectionLineType.SmoothStep,23animated: true,24},25{26id: "e2-3",27source: "2",28target: "3",29type: ConnectionLineType.SmoothStep,30animated: true,31},32{33id: "e3-4",34source: "3",35target: "4",36type: ConnectionLineType.SmoothStep,37animated: true,38},39];404142const 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:
1const Children = () => {2const channelRef = useRef<Channel | null>(null);3const roomRef = useRef<Room | null>(null);4const initializedRef = useRef(false);5const commentsRef = useRef<Comments | null>(null);6const mousePointersRef = useRef<MousePointers | null>(null);78const { x, y, zoom } = useViewport();9const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);10const [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:
1const initialize = useCallback(async () => {2if (initializedRef.current) return;34initializedRef.current = true;56try {7const participantId = generateId();8const realtime = new Realtime(DEVELOPER_TOKEN, {9participant: {10id: participantId11},12});1314channelRef.current = await realtime.connect("react-flow-nodes-sync");15channelRef.current!.subscribe<{ edge: Edge }>(16"new-edge",17({ data, participantId: senderId }) => {18if (senderId === participantId) return;1920setEdges((eds) => addEdge(data.edge, eds));21}22);2324channelRef.current!.subscribe<{ node: Node }>(25"node-drag",26({ data, participantId: senderId }) => {27if (senderId === participantId) return;2829setNodes((nds) =>30nds.map((node) =>31node.id === data.node.id ? { ...node, ...data.node } : node32)33);34}35);3637const room = await createRoom({38developerToken: DEVELOPER_TOKEN,39roomId: "ROOM_ID",40participant: {41id: participantId,42name: "Participant",43},44group: {45id: "GROUP_ID",46name: "GROUP_NAME",47},48});4950mousePointersRef.current = new MousePointers('react-flow-container')51const pin = new HTMLPin('react-flow-container', {52dataAttributeName: "data-id",53dataAttributeValueFilters: [/.*null-(target|source)$/],54})5556commentsRef.current = new Comments(pin, {57buttonLocation: 'comments'58})5960room.addComponent(commentsRef.current)61room.addComponent(mousePointersRef.current)6263roomRef.current = room;64} catch (error) {65console.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:
1const onDragOver = useCallback(2(event: React.DragEvent<HTMLButtonElement | HTMLDivElement>) => {3event.preventDefault();4event.dataTransfer.dropEffect = "move";5},6[]7);89const onNodeDrag = useCallback(10(_: React.MouseEvent, node: Node) => {11channelRef.current?.publish("node-drag", { node });12},13[channelRef]14);1516const onConnect = useCallback(17(connection: Connection) => {18const edge: Edge = {19...connection,20type: ConnectionLineType.SmoothStep,21animated: true,22};2324setEdges((eds) => addEdge(edge, eds));2526channelRef.current!.publish("new-edge", {27edge,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:
1useEffect(() => {2if(!mousePointersRef.current) return;34mousePointersRef.current.transform({5translate: {6x: x,7y: y,8},9scale: zoom,10});11}, [x, y, zoom]);1213useEffect(() => {14initialize();1516return () => {17if(roomRef.current) {18roomRef.current.leave();19}2021if(channelRef.current) {22channelRef.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:
1return (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<ReactFlow10nodes={nodes}11onConnect={onConnect}12onDragOver={onDragOver}13onNodeDrag={onNodeDrag}14edges={edges}15onNodesChange={onNodesChange}16onEdgesChange={onEdgesChange}17connectionMode={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:
1const App = () => {2return (3<ReactFlowProvider>4<Children />5</ReactFlowProvider>6)7}89export 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:
- 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.
- SuperViz Room
- The Room created with createRoom provides the foundation for collaboration features.
- It manages participant information and provides the context for collaboration components.
- 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.
- 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:
1npm 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.