A Panel wrapper for the React Flow JS library.
- Python-first, JSON-serializable graph state
- Panel viewables inside nodes via node
viewentries - Interactive editing (drag/select/connect/delete) with sync back to Python
- Optional schema definitions for node/edge properties
- Event callbacks via
ReactFlow.on(...)for app-level handling (callback signature:callback(payload, flow))
This project is in its early stages, so if you find a version that suits your needs, it’s recommended to pin your version, as updates may introduce changes.
Install it via pip:
pip install panel-reactflowimport panel as pn
from panel_reactflow import ReactFlow
pn.extension("jsoneditor")
nodes = [
{
"id": "n1",
"position": {"x": 0, "y": 0},
"type": "panel",
"label": "Start",
"data": {},
"view": pn.pane.Markdown("Node 1 content"),
},
{
"id": "n2",
"position": {"x": 260, "y": 60},
"type": "panel",
"label": "End",
"data": {},
"view": pn.pane.Markdown("Node 2 content"),
},
]
edges = [
{"id": "e1", "source": "n1", "target": "n2"},
]
flow = ReactFlow(
nodes=nodes,
edges=edges,
)
flowFor property schemas and richer editors, provide node_types/edge_types with PropertySpec and handle changes via ReactFlow.on(...). .on callbacks receive the event payload as the first argument and can optionally accept the ReactFlow instance as a second argument.
Connection checks are opt-in. For frontend-only validation, set connection_validation with any of direction, types, capacity, duplicates, or cycles set to True. types compares declared handle types case-insensitively and permits unknown types; capacity uses maxConnections on input handle dictionaries, with no limit when omitted. Existing handle connectability flags still apply.
from panel_reactflow import NodeType, ReactFlow
flow = ReactFlow(
node_types={
"source": NodeType(type="source", outputs=[{"id": "value", "type": "str"}]),
"sink": NodeType(type="sink", inputs=[{"id": "value", "type": "str", "maxConnections": 1}]),
},
connection_validation={"direction": True, "types": True, "capacity": True, "cycles": True},
)For application rules, register a Python validator. On each drag start, ReactFlow requests results for every candidate port and shows the returned reasons while dragging. The callback receives an edge-shaped payload with source, target, sourceHandle, and targetHandle; return None to allow or a string to reject. Hooks and frontend checks can be used together. Validate again in your edge_added handler before accepting the connection, since the graph may have changed after drag start.
def validate_connection(edge, flow):
if edge["source"] == edge["target"]:
return "A node cannot connect to itself."
flow.add_connection_validator(validate_connection)Run the connection validation demo with PYTHONPATH=src pixi run panel serve examples/connection_validation.py --show from the repository root.
git clone https://github.com/panel-extensions/panel-reactflow
cd panel-reactflowFor a simple setup use uv:
uv venv
source .venv/bin/activate # on linux. Similar commands for windows and osx
uv pip install -e .[dev]
pre-commit run install
pytest testsFor the full Github Actions setup use pixi:
pixi run pre-commit-install
pixi run postinstall
pixi run testThis repository is based on copier-template-panel-extension (you can create your own Panel extension with it)!
To update to the latest template version run:
pixi exec --spec copier --spec ruamel.yaml -- copier update --defaults --trustNote: copier will show Conflict for files with manual changes during an update. This is normal. As long as there are no merge conflict markers, all patches applied cleanly.
Contributions are welcome! Please follow these steps to contribute:
- Fork the repository.
- Create a new branch:
git checkout -b feature/YourFeature. - Make your changes and commit them:
git commit -m 'Add some feature'. - Push to the branch:
git push origin feature/YourFeature. - Open a pull request.
Please ensure your code adheres to the project's coding standards and passes all tests.
