Today I want to present to you this powerful tool for creating a node-based application: the React Flow. It is a library for building anything from simple static diagrams to data visualizations to complex visual editors. In this article I will show you how to create a simple React Flow application, understand more about nodes and edges, and add real-time synchronization between participants in the same content.

Installation

To begin with React Flow, you first will need to have a React base application and add the package reactflow to it, you can use any package manager

1
npm install reactflow

Nodes and Edges

Before starting to use it, you should be familiarized with what a node and an edge is.

In simple terms, nodes are the primary elements or objects that you want to represent in your graph, and edges are the connections or relationships between these nodes. For example, in a social network, each person could be a node, and the friendships between them could be the edges.

Technically speaking, React Flow has these two types: the Node, which will contain an ID, a position and its data, and the Edge which will contain an ID for it, a source and target. In the code below I created a list of initial nodes (a couple of blocks) and a list of initial edges: connecting the first node to the second node.

1
export const initialNodes = [
2
{
3
id: '1',
4
position: { x: 200, y: 200 },
5
data: { label: 'First block' },
6
},
7
{
8
id: '2',
9
position: { x: 250, y: 300 },
10
data: { label: 'Second block' }
11
},
12
]
13
14
export const initialEdges = [
15
{
16
id: 'some-id',
17
source: '1',
18
target: '2'
19
}
20
]

Start using React Flow

After creating the initial nodes and edges, you are now able to add the ReactFlow component to your application. Such as shown below:

1
import React from 'react';
2
import ReactFlow from 'reactflow';
3
4
import 'reactflow/dist/style.css';
5
6
export default function App() {
7
return (
8
<div style={{ width: '100vw', height: '100vh' }}>
9
<ReactFlow nodes={initialNodes} edges={initialEdges} />
10
</div>
11
);
12
}

In the code above we create a full screen container for our nodes. We also import the styles from reactflow/dist/style.css.

To make manipulation of the data easy, I’m going to use the useNodesState and useEdgesState hooks, which work like the useState hook. With an addiction to a callback function when there are changes on the state.

1
import ReactFlow, { useNodesState, useEdgesState } from 'reactflow'
2
3
export default function App() {
4
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
5
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
6
7
return (
8
<div style={{ width: '100vw', height: '100vh' }}>
9
<ReactFlow nodes={nodes} edges={edges} />
10
</div>
11
);
12
}

To create a better experience for our users, let’s add some controls, allowing them to zoom in and out, to see a mini map of their content and other features. We will add it as a child to our ReactFlow component.

1
<ReactFlow nodes={nodes} edges={edges}>
2
<Controls />
3
<MiniMap />
4
</ReactFlow>

To make a great background, you can also add the Background element, in which you will be able to choose variants of background like dots, crosses or lines as well their colors.

1
<Background variant={BackgroundVariant.Cross} color='#f00' />

Synchronizing between users

If you want to create a collaborative environment where multiple users can interact with the same React Flow application at the same time, you will need to use SuperViz Real-time. This service enables real-time data synchronization, allowing changes made by one user to be instantly visible to all other users in the same channel. This involves creating a shared channel for each group of users and synchronizing their activities.

Initializing the Real-time Client

To begin with SuperViz Real-time, you first need to initialize the Realtime client with your Developer Key and participant information.

To initialize the Realtime client, you need to import the Realtime class from @superviz/realtime package, which takes the Developer Key and an object as parameters:

  • DEVELOPER_KEY: Your SuperViz API key obtained from the SuperViz dashboard.
  • participant: An object that contains information about the current user, such as id (required) and name (optional).

Here's an example of how to initialize real-time communication with SuperViz:

1
// Import the SuperViz real-time package
2
import { Realtime } from '@superviz/realtime';
3
4
// Initialize the real-time client with your developer key and participant info
5
const realtime = new Realtime('DEVELOPER_KEY', {
6
participant: {
7
id: 'PARTICIPANT_ID',
8
name: 'Participant Name', // Optional
9
},
10
});
11
12
// Connect to a specific channel
13
const channel = await realtime.connect('reactflow-room-123');

Connecting to a Channel

After initializing the Realtime client, you need to connect to a channel. Channels are a fundamental concept in real-time communication, used to organize messages based on specific topics or themes. Multiple participants can connect to the same channel to share data in real-time.

1
const realtime = new Realtime();
2
room.addComponent(realtime);

Subscribing to Events

To achieve full synchronization, we need to listen to specific events happening in our React Flow application. In our case, we'll subscribe to two primary events: new-edge (when users connect nodes) and node-drag (when users move nodes around the canvas).

Let's first create a function to handle when new edges are added, and then subscribe it to the new-edge event on our channel.

1
function onNewEdgesAdded({data, participantId}){
2
// Verify if the origin of the event dispatch isn't the same participant on the page
3
// preventing a infinity loop
4
if (participantId === currentParticipantId) return;
5
6
setEdges((eds) => addEdge(data.edge, eds));
7
}
8
9
realtime.subscribe('new-edge', onNewEdgesAdded)

Similarly, we'll create a handler for the node-drag event. When we receive position updates from other participants, we'll update our local nodes accordingly:

1
function onNodeDrag(event, node) {
2
realtime.publish('node-drag', { node });
3
}

Publishing Events

Now that we're subscribing to events, we need to publish them when local changes occur. We can do this by using the publish method on our channel. Whenever a user adds a new edge or drags a node, we'll publish the corresponding event, which will then be received by all other participants connected to the same channel.

To detect when these changes occur, we'll use React Flow's callback functions onConnectand onNodeDrag:

1
<ReactFlow
2
...
3
onNodeDrag={onNodeDrag}
4
onConnect={onConnect}>
5
</ReactFlow>

The implementation of the onNodeDrag is straightforward, as shown below:

1
function onNodeDrag(event, node) {
2
realtime.publish('node-drag', { node });
3
}

To publish the new-edge event on a new connection (onConnect) it’s a bit different, we first need to recreate the edge object, add it to our current list using the React Flow’s addEdge method, and then publish to SuperViz Real-time.

1
function onConnect(params) => {
2
const edge = {
3
...params,
4
type: ConnectionLineType.SmoothStep,
5
animated: true,
6
}
7
8
setEdges((edges) => addEdge(edge, edges))
9
10
realtime.publish('new-edge', { edge })
11
}

Conclusion

In this tutorial, we've learned the basics of React Flow and how to create a real-time, collaborative environment using SuperViz Real-time. The key concepts we covered include initializing the Realtime client, connecting to channels, subscribing to events, and publishing updates. You can see this project in action on our demo page dedicated to React Flow, as well as the source code on our GitHub repository.

Happy coding!