> ## Documentation Index
> Fetch the complete documentation index at: https://docs.velt.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# React Flow Editor

> Setup Multiplayer Editing for React Flow diagrams.

The `@veltdev/reactflow-crdt` library enables real-time collaborative editing on React Flow diagrams. The collaboration editing engine is built on top of [Yjs](https://docs.yjs.dev/) and Velt SDK.

## Prerequisites

* Node.js (v14 or higher)
* React (v16.8 or higher for hooks)
* A Velt account with an API key ([sign up](https://console.velt.dev/))
* Optional: TypeScript for type safety

## Setup

### Step 1: Install Dependencies

Install the required packages:

```bash theme={null}
npm install @veltdev/reactflow-crdt @veltdev/react
```

### Step 2: Setup Velt

Initialize the Velt client by following the [Velt Setup Docs](/get-started/quickstart). This is required for the collaboration engine to work.

### Step 3: Initialize Velt CRDT Extension

* Initialize the Velt CRDT extension and use it to initialize the React Flow diagram.
* Pass an `editorId` to uniquely identify each diagram instance you have in your app. This is especially important when you have multiple diagrams in your app.

```jsx theme={null}
const { nodes, edges, onNodesChange, onEdgesChange, onConnect } = useVeltReactFlowCrdtExtension({
  editorId: 'YOUR_EDITOR_ID',
  initialNodes: [{ id: '1', data: { label: 'Start' }, position: { x: 0, y: 0 } }],
  initialEdges: []
});

return (
  <ReactFlow
    nodes={nodes}
    edges={edges}
    onNodesChange={onNodesChange}
    onEdgesChange={onEdgesChange}
    onConnect={onConnect}
    fitView
  >
    <Background />
  </ReactFlow>
);
```

## Notes

* **Unique editorId**: Use a unique `editorId` per diagram instance.
* **Use CRDT handlers**: Always apply the store-provided `nodes`, `edges`, `onNodesChange`, `onEdgesChange`, and `onConnect`.

## Testing and Debugging

**To test collaboration:**

1. Login two unique users on two browser profiles and open the same page in both.
2. Move nodes or create connections in one window and verify they sync in the other.

**Common issues:**

* Nodes/edges not syncing: Ensure each diagram has a unique `editorId` and the Velt client is initialized. Also ensure that you are logged in with two different users in two different browser profiles.
* No updates on connect: Use the provided CRDT-aware `nodes`, `edges`, `onNodesChange`/`onEdgesChange` and `onConnect` handlers from the store.

<Tip>
  Use the [VeltCrdtStoreMap debugging interface](/realtime-collaboration/crdt/setup/core#veltcrdtstoremap) to inspect and monitor your CRDT stores in real-time from the browser console.
</Tip>

## Complete Example

<Tabs>
  <Tab title="React / Next.js">
    [Open Live Demo In Larger Window](https://sample-apps-reactflow-demo.vercel.app/) | [View Demo Repo](https://github.com/velt-js/sample-apps/tree/main/apps/react/crdt/canvas/reactflow/reactflow-demo)

    ```jsx Complete Implementation expandable lines theme={null}
    import {
        Background,
        ReactFlow,
        ReactFlowProvider,
        useReactFlow,
        type Edge
    } from '@xyflow/react';
    import { useCallback, useRef } from 'react';
    import { useVeltInitState } from '@veltdev/react';
    import { useVeltReactFlowCrdtExtension } from '@veltdev/reactflow-crdt';
    import '@xyflow/react/dist/style.css';

    const initialNodes = [
        {
            id: '0',
            type: 'input',
            data: { label: 'Node' },
            position: { x: 0, y: 50 },
        },
    ];
    const initialEdges: Edge[] = [];

    let id = 9;
    const getId = () => `${id++}`;
    const nodeOrigin: [number, number] = [0.5, 0];

    const AddNodeOnEdgeDrop = () => {
        const { nodes, edges, onNodesChange, onEdgesChange, onConnect } = useVeltReactFlowCrdtExtension({
            editorId: 'YOUR_EDITOR_ID',
            initialEdges,
            initialNodes,
        });

        const reactFlowWrapper = useRef(null);
        const { screenToFlowPosition } = useReactFlow();

        const onConnectEnd = useCallback(
            (event: any, connectionState: any) => {
                if (!connectionState.isValid) {
                    const id = getId();
                    const { clientX, clientY } = 'changedTouches' in event ? event.changedTouches[0] : event;
                    const newNode = {
                        id,
                        position: screenToFlowPosition({ x: clientX, y: clientY }),
                        data: { label: `Node ${id}` },
                        origin: [0.5, 0.0],
                    };

                    // Add new node using CRDT-aware change handler
                    onNodesChange([{ type: 'add', item: newNode }]);

                    // Add new edge using CRDT-aware change handler
                    const newEdge = {
                        id,
                        source: connectionState.fromNode.id,
                        target: id,
                    };
                    onEdgesChange([{ type: 'add', item: newEdge }]);
                }
            },
            [screenToFlowPosition, onNodesChange, onEdgesChange],
        );

        return (
            <div className="react-flow-container" ref={reactFlowWrapper} >
                <ReactFlow
                    style={{ backgroundColor: "#F7F9FB" }}
                    nodes={nodes}
                    edges={edges}
                    onNodesChange={onNodesChange}  // CRDT-synced node changes
                    onEdgesChange={onEdgesChange}  // CRDT-synced edge changes
                    onConnect={onConnect}  // CRDT-synced connections
                    onConnectEnd={onConnectEnd}  // Custom handler for adding nodes/edges
                    fitView
                    fitViewOptions={{ padding: 2 }
                    }
                    nodeOrigin={nodeOrigin}
                >
                    <Background />
                </ReactFlow>
            </div>
        );
    };

    function ReactFlowComponent() {
        const veltInitialized = useVeltInitState();

        if (!veltInitialized) {
            return <div>Loading...</div>;
        }

        return (
            <ReactFlowProvider>
                <AddNodeOnEdgeDrop />
            </ReactFlowProvider>
        );
    }

    export default ReactFlowComponent;
    ```
  </Tab>

  <Tab title="Live Demo">
    [Open Live Demo In Larger Window](https://sample-apps-reactflow-demo.vercel.app/) | [View Demo Repo](https://github.com/velt-js/sample-apps/tree/main/apps/react/crdt/canvas/reactflow/reactflow-demo)

    <Frame>
      <iframe src="https://sample-apps-reactflow-demo.vercel.app/" className="w-full" height="500px" />
    </Frame>
  </Tab>
</Tabs>

## APIs

### Custom Encryption

You can encrypt CRDT data before it’s stored in Velt by registering a custom encryption provider. For CRDT methods, input data is of type `Uint8Array | number[]`.

<Tabs>
  <Tab title="React / Next.js">
    ```ts theme={null}
    async function encryptData(config: EncryptConfig<number[]>): Promise<string> {
      const encryptedData = await yourEncryptDataMethod(config.data);
      return encryptedData;
    }

    async function decryptData(config: DecryptConfig<string>): Promise<number[]> {
      const decryptedData = await yourDecryptDataMethod(config.data);
      return decryptedData;
    }

    const encryptionProvider: VeltEncryptionProvider<number[], string> = {
      encrypt: encryptData,
      decrypt: decryptData,
    };

    <VeltProvider
      apiKey="YOUR_API_KEY"
      encryptionProvider={encryptionProvider}
    />

    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```ts theme={null}
    async function encryptData(config: EncryptConfig<number[]>): Promise<string> {
      const encryptedData = await yourEncryptDataMethod(config.data);
      return encryptedData;
    }

    async function decryptData(config: DecryptConfig<string>): Promise<number[]> {
      const decryptedData = await yourDecryptDataMethod(config.data);
      return decryptedData;
    }

    const encryptionProvider: VeltEncryptionProvider<number[], string> = {
      encrypt: encryptData,
      decrypt: decryptData,
    };

    client.setEncryptionProvider(encryptionProvider);
    ```
  </Tab>
</Tabs>

See also: [setEncryptionProvider()](/api-reference/sdk/api/api-methods#setencryptionprovider-encryptionprovider)
· [VeltEncryptionProvider](/api-reference/sdk/models/data-models#veltencryptionprovider)
· [EncryptConfig](/api-reference/sdk/models/data-models#encryptconfig)
· [DecryptConfig](/api-reference/sdk/models/data-models#decryptconfig)

### [useVeltReactFlowCrdtExtension()](/api-reference/sdk/api/api-methods#useveltreactflowcrdtextension)

Provides real-time collaborative editing on React Flow diagrams with Yjs and Velt SDK.

* Signature: `useVeltReactFlowCrdtExtension(config: VeltReactFlowCrdtExtensionConfig)`
* Params: [VeltReactFlowCrdtExtensionConfig](/api-reference/sdk/models/data-models#veltreactflowcrdtextensionconfig)
  * `editorId`: Unique identifier for the diagram instance.
  * `initialNodes`: Initial set of nodes.
  * `initialEdges`: Initial set of edges.
  * `debounceMs`: Debounce time for update propagation (ms).
* Returns: [VeltReactFlowCrdtExtensionResponse](/api-reference/sdk/models/data-models#veltreactflowcrdtextensionresponse)
  * `store`: React Flow CRDT store instance.
  * `nodes`: Array of nodes in the diagram.
  * `edges`: Array of edges in the diagram.
  * `onNodesChange`: CRDT-aware handler to apply node changes.
  * `onEdgesChange`: CRDT-aware handler to apply edge changes.
  * `onConnect`: CRDT-aware handler to apply connect changes.
  * `setNodes`: Method to set nodes
  * `setEdges`: Method to set edges

### [onNodesChange()](/api-reference/sdk/api/api-methods#onnodeschange)

CRDT-aware handler to apply node changes (add/update/remove) that sync across collaborators.

* Params: [NodeChange\[\]](/api-reference/sdk/models/data-models#nodechange)
* Returns: `void`

```tsx theme={null}
onNodesChange([{ type: 'add', item: newNode }]);
```

### [onEdgesChange()](/api-reference/sdk/api/api-methods#onedgeschange)

CRDT-aware handler to apply edge changes (add/update/remove) that sync across collaborators.

* Params: [EdgeChange\[\]](/api-reference/sdk/models/data-models#edgechange)
* Returns: `void`

```tsx theme={null}
onEdgesChange([{ type: 'add', item: newEdge }]);
```

### [onConnect()](/api-reference/sdk/api/api-methods#onconnect)

CRDT-aware connect handler compatible with React Flow’s `<ReactFlow onConnect={...} />`.

* Params: [Connection](/api-reference/sdk/models/data-models#connection)
* Returns: `void`

```tsx theme={null}
<ReactFlow onConnect={onConnect} />
```

### [setNodes()](/api-reference/sdk/api/api-methods#setnodes)

Imperative setter for nodes (useful for non-event updates). Synced via the store.

* Params: [Node\[\]](/api-reference/sdk/models/data-models#node-react-flow)
* Returns: `void`

```tsx theme={null}
setNodes(prev => [...prev, myNode]);
```

### [setEdges()](/api-reference/sdk/api/api-methods#setedges)

Imperative setter for edges (useful for non-event updates). Synced via the store.

* Params: [Edge\[\]](/api-reference/sdk/models/data-models#edge-react-flow)
* Returns: `void`

```tsx theme={null}
setEdges(prev => [...prev, myEdge]);
```
