Devtools and Debugging

This is an ongoing experiment on implementing our own React Flow devtools. While we are working on the actual package, we’d love to hear about your feedback and ideas on Discord or via mail at [email protected].

React Flow can often seem like a magic black box, but in reality you can reveal quite a lot about its internal state if you know where to look. In this guide we will show you three different ways to reveal the internal state of your flow:

  • A <ViewportLogger /> component that shows the current position and zoom level of the viewport.
  • A <NodeInspector /> component that reveals the state of each node.
  • A <ChangeLogger /> that wraps your flow’s onNodesChange handler and logs each change as it is dispatched.

While we find these tools useful for making sure React Flow is working properly, you might also find them useful for debugging your applications as your flows and their interactions become more complex.

import { useCallback } from 'react';
import {
  ReactFlow,
  addEdge,
  useEdgesState,
  useNodesState,
  type Edge,
  type OnConnect,
  type Node,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
import DevTools from './Devtools';
 
const initNodes: Node[] = [
  {
    id: '1a',
    type: 'input',
    data: { label: 'Node 1' },
    position: { x: 250, y: 5 },
  },
  {
    id: '2a',
    data: { label: 'Node 2' },
    position: { x: 100, y: 120 },
  },
  {
    id: '3a',
    data: { label: 'Node 3' },
    position: { x: 400, y: 120 },
  },
];
 
const initEdges: Edge[] = [
  { id: 'e1-2', source: '1a', target: '2a' },
  { id: 'e1-3', source: '1a', target: '3a' },
];
 
const fitViewOptions = { padding: 0.5 };
 
function Flow() {
  const [nodes, , onNodesChange] = useNodesState(initNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges);
 
  const onConnect: OnConnect = useCallback(
    (params) => setEdges((eds) => addEdge(params, eds)),
    [setEdges],
  );
 
  return (
    <ReactFlow
      nodes={nodes}
      edges={edges}
      onNodesChange={onNodesChange}
      onEdgesChange={onEdgesChange}
      onConnect={onConnect}
      fitView
      fitViewOptions={fitViewOptions}
    >
      <DevTools />
    </ReactFlow>
  );
}
 
export default Flow;

We encourage you to copy any or all of the components from this example into your own projects and modify them to suit your needs: each component works independently!

Node Inspector

The <NodeInspector /> component makes use of our useNodes hook to access all the nodes in the flow. Typically we discourage using this hook because it will trigger a re-render any time any of your nodes change, but that’s exactly what makes it so useful for debugging!

The width and height properties are added to each node by React Flow after it has measured the node’s dimensions. We pass those dimensions, as well as other information like the node’s id and type, to a custom <NodeInfo /> component.

We make use of the <ViewportPortal /> component to let us render the inspector into React Flow’s viewport. That means it’s content will be positioned and transformed along with the rest of the flow as the user pans and zooms.

Change Logger

Any change to your nodes and edges that originates from React Flow itself is communicated to you through the onNodesChange and onEdgesChange callbacks. If you are working with a controlled flow (that means you’re managing the nodes and edges yourself), you need to apply those changes to your state in order to keep everything in sync.

The <ChangeLogger /> component wraps your user-provided onNodesChange handler with a custom function that intercepts and logs each change as it is dispatched. We can do this by using the useStore and useStoreApi hooks to access the store and and then update React Flow’s internal state accordingly. These two hooks give you powerful access to React Flow’s internal state and methods.

Beyond debugging, using the <ChangeLogger /> can be a great way to learn more about how React Flow works and get you thinking about the different functionality you can build on top of each change.

You can find documentation on the NodeChange and EdgeChange types in the API reference.

Viewport Logger

The <ViewportLogger /> is the simplest example of what state you can pull out of React Flow’s store if you know what to look for. The state of the viewport is stored internally under the transform key (a name we inherited from d3-zoom). This component extracts the x, y, and zoom components of the transform and renders them into a <Panel /> component.

Let us know what you think

As mentioned above, if you have any feedback or ideas on how to improve the devtools, please let us know on Discord or via mail at [email protected]. If you build your own devtools using these ideas, we’d love to hear about it!