Full documentation: https://flows.2plot.dev — the dedicated dash-flows documentation site, with the complete API reference and deeper examples. This page is the quick-start overview.
Overview
Dash Flows is a powerful React Flow 12.3.5+ integration for Plotly Dash that enables developers to create interactive, node-based flow diagrams and visual workflows. Build data pipelines, process flows, org charts, and complex graph visualizations with ease.
Key Features
| Feature | Description |
|---|---|
| 6 Node Types | Input, Output, Default, Group, Toolbar, Resizable, Circle |
| 7 Edge Types | Bezier, Straight, Step, SmoothStep, Button, Data, AnimatedSVG |
| Custom Icons | DashIconify integration with flexible layouts |
| Auto Layouts | ELK.js algorithms (layered, force, radial, stress) |
| Rich Callbacks | Click, double-click, hover, context menu, selection events |
| Theming | Glass, solid, minimal presets with 6 color schemes |
| Dark Mode | Full support via Mantine integration |
Installation
pip install dash-flows
Optional dependencies for enhanced features:
pip install dash-iconify # For custom icons
pip install dash-mantine-components # For theming & UI
Quick Start
Get started with a minimal working example. The key requirements are:
- Define nodes with
id,type,data, andposition - Define edges with
id,source, andtarget - Set an explicit height on the component
# File: docs/dash_flows/introduction.py
"""
Dash Flows - Quick Start Introduction
=====================================
A minimal working example to get started with dash-flows.
"""
import dash_mantine_components as dmc
from dash import html
import dash_flows
# Define a simple 3-node flow
nodes = [
{
"id": "start",
"type": "input",
"data": {"label": "Start", "sublabel": "Entry point"},
"position": {"x": 50, "y": 50},
},
{
"id": "process",
"type": "default",
"data": {"label": "Process", "sublabel": "Transform data"},
"position": {"x": 50, "y": 150},
},
{
"id": "end",
"type": "output",
"data": {"label": "End", "sublabel": "Output result"},
"position": {"x": 50, "y": 250},
},
]
# Connect nodes with edges
edges = [
{"id": "e1", "source": "start", "target": "process", "animated": True},
{"id": "e2", "source": "process", "target": "end", "animated": True},
]
component = dmc.Paper(
[
dmc.Text(
"A minimal flow diagram with input, process, and output nodes.",
c="dimmed",
size="sm",
mb="md",
),
dash_flows.DashFlows(
id="dash-flows-intro",
nodes=nodes,
edges=edges,
style={"height": "350px"},
fitView=True,
showControls=True,
showMiniMap=True,
),
dmc.Alert(
[
dmc.Text("Key requirements:", size="sm", fw=500),
dmc.List(
[
dmc.ListItem("Each node needs: id, type, data, position"),
dmc.ListItem("Each edge needs: id, source, target"),
dmc.ListItem("Container must have explicit height"),
],
size="sm",
),
],
color="blue",
mt="md",
),
],
p="md",
withBorder=True,
radius="md",
)
Basic Concepts
Understanding the fundamental building blocks of Dash Flows:
Node Structure
Every node requires these properties:
{
"id": "unique-id", # Required: Unique identifier
"type": "default", # Required: Node type
"data": {"label": "Text"}, # Required: Node content
"position": {"x": 100, "y": 50} # Required: Initial position
}
Edge Structure
Edges connect nodes together:
{
"id": "edge-1", # Required: Unique identifier
"source": "node-1", # Required: Source node ID
"target": "node-2", # Required: Target node ID
"animated": True, # Optional: Animated dashed line
"label": "Step 1", # Optional: Edge label
"type": "smoothstep" # Optional: Edge style
}
# File: docs/dash_flows/basic_nodes_edges.py
"""
Dash Flows - Basic Nodes and Edges
==================================
Understanding the fundamental building blocks of flow diagrams.
"""
import dash_mantine_components as dmc
from dash import html
import dash_flows
# Nodes with detailed structure
nodes = [
{
"id": "node-1",
"type": "default",
"data": {"label": "Start Node"},
"position": {"x": 100, "y": 50},
},
{
"id": "node-2",
"type": "default",
"data": {"label": "Process A"},
"position": {"x": 50, "y": 150},
},
{
"id": "node-3",
"type": "default",
"data": {"label": "Process B"},
"position": {"x": 200, "y": 150},
},
{
"id": "node-4",
"type": "default",
"data": {"label": "End Node"},
"position": {"x": 125, "y": 250},
},
]
# Various edge configurations
edges = [
{
"id": "e1-2",
"source": "node-1",
"target": "node-2",
"animated": True, # Animated dashed line
},
{
"id": "e1-3",
"source": "node-1",
"target": "node-3",
},
{
"id": "e2-4",
"source": "node-2",
"target": "node-4",
"label": "Step 1", # Edge with label
},
{
"id": "e3-4",
"source": "node-3",
"target": "node-4",
"label": "Step 2",
},
]
component = dmc.Paper(
[
dmc.Text(
"Node and edge structure demonstration with labels and animations.",
c="dimmed",
size="sm",
mb="md",
),
dmc.SimpleGrid(
cols={"base": 1, "md": 2},
spacing="md",
mb="md",
children=[
dmc.Paper(
[
dmc.Text("Node Structure", fw=600, size="sm", mb="xs"),
dmc.Code(
block=True,
children="""{
"id": "unique-id",
"type": "default",
"data": {"label": "Text"},
"position": {"x": 100, "y": 50}
}""",
),
],
p="sm",
withBorder=True,
),
dmc.Paper(
[
dmc.Text("Edge Structure", fw=600, size="sm", mb="xs"),
dmc.Code(
block=True,
children="""{
"id": "edge-1",
"source": "node-1",
"target": "node-2",
"animated": True,
"label": "Step 1"
}""",
),
],
p="sm",
withBorder=True,
),
],
),
dash_flows.DashFlows(
id="dash-flows-basic",
nodes=nodes,
edges=edges,
style={"height": "350px"},
fitView=True,
showControls=True,
showMiniMap=True,
),
],
p="md",
withBorder=True,
radius="md",
)
Node Types
Dash Flows provides 6 built-in node types, each designed for specific use cases:
| Type | Description | Handles |
|---|---|---|
input | Entry point nodes (green accent) | Source only |
output | Exit point nodes (purple accent) | Target only |
default | Standard processing nodes | Both |
group | Container for child nodes | None (container) |
toolbar | Nodes with floating action toolbar | Both |
resizable | User-resizable nodes | Configurable |
circle | Small animated circular indicators | Both |
Edge Types
Connect nodes with various edge styles:
| Type | Description |
|---|---|
default / bezier | Smooth curved connections |
straight | Direct line connections |
step | Right-angle with sharp corners |
smoothstep | Right-angle with rounded corners |
button | Edge with interactive delete button |
data | Edge displaying data labels inline |
animatedsvg | Flowing animated shapes |
# File: docs/dash_flows/edge_types.py
"""
Dash Flows - Edge Types
=======================
Demonstrates all available edge connection styles.
"""
import dash_mantine_components as dmc
from dash import html
import dash_flows
# Create nodes for demonstrating different edge types
nodes = [
# Row 1 - Connection style demos
{"id": "n1", "type": "default", "data": {"label": "Bezier"}, "position": {"x": 50, "y": 50}},
{"id": "n2", "type": "default", "data": {"label": "Target"}, "position": {"x": 200, "y": 50}},
{"id": "n3", "type": "default", "data": {"label": "Straight"}, "position": {"x": 50, "y": 120}},
{"id": "n4", "type": "default", "data": {"label": "Target"}, "position": {"x": 200, "y": 120}},
{"id": "n5", "type": "default", "data": {"label": "Step"}, "position": {"x": 50, "y": 190}},
{"id": "n6", "type": "default", "data": {"label": "Target"}, "position": {"x": 200, "y": 190}},
{"id": "n7", "type": "default", "data": {"label": "SmoothStep"}, "position": {"x": 50, "y": 260}},
{"id": "n8", "type": "default", "data": {"label": "Target"}, "position": {"x": 200, "y": 260}},
# Row 2 - Special edge types
{"id": "n9", "type": "default", "data": {"label": "Button Edge"}, "position": {"x": 350, "y": 50}},
{"id": "n10", "type": "default", "data": {"label": "Target"}, "position": {"x": 500, "y": 50}},
{"id": "n11", "type": "default", "data": {"label": "Data Edge"}, "position": {"x": 350, "y": 140}},
{"id": "n12", "type": "default", "data": {"label": "Target"}, "position": {"x": 500, "y": 140}},
{"id": "n13", "type": "default", "data": {"label": "Animated"}, "position": {"x": 350, "y": 230}},
{"id": "n14", "type": "default", "data": {"label": "Target"}, "position": {"x": 500, "y": 230}},
]
edges = [
# Standard connection styles
{
"id": "e-bezier",
"source": "n1",
"target": "n2",
"type": "default", # or "bezier" - smooth curve
"label": "default",
},
{
"id": "e-straight",
"source": "n3",
"target": "n4",
"type": "straight", # Direct line
"label": "straight",
},
{
"id": "e-step",
"source": "n5",
"target": "n6",
"type": "step", # Right angles, sharp corners
"label": "step",
},
{
"id": "e-smoothstep",
"source": "n7",
"target": "n8",
"type": "smoothstep", # Right angles, rounded corners
"label": "smoothstep",
},
# Special edge types
{
"id": "e-button",
"source": "n9",
"target": "n10",
"type": "button", # Has delete button
},
{
"id": "e-data",
"source": "n11",
"target": "n12",
"type": "data", # Shows data inline
"data": {"label": "42 items"},
},
{
"id": "e-animated",
"source": "n13",
"target": "n14",
"animated": True, # Animated dashed line
"style": {"stroke": "#10b981"},
},
]
component = dmc.Paper(
[
dmc.Text(
"Different edge styles for connecting nodes.",
c="dimmed",
size="sm",
mb="md",
),
dash_flows.DashFlows(
id="dash-flows-edge-types",
nodes=nodes,
edges=edges,
style={"height": "380px"},
fitView=True,
showControls=True,
showMiniMap=False,
),
dmc.Table(
data={
"head": ["Edge Type", "Description", "Use Case"],
"body": [
["default/bezier", "Smooth curved line", "General connections"],
["straight", "Direct line", "Simple relationships"],
["step", "Right angles, sharp", "Flowcharts"],
["smoothstep", "Right angles, rounded", "Modern flowcharts"],
["button", "Has delete button", "Editable flows"],
["data", "Shows inline data", "Data pipelines"],
["animated", "Animated dashes", "Active/processing"],
],
},
striped=True,
mt="md",
),
],
p="md",
withBorder=True,
radius="md",
)
Custom Icons
Enhance your nodes with custom icons using DashIconify. Supports flexible layouts and content-aware sizing.
Icon Props
| Prop | Type | Description |
|---|---|---|
icon | DashIconify | Icon component to display |
iconColor | str | Background color for icon container |
showIcon | bool | Toggle icon visibility |
layout | str | "stacked" (vertical) or "horizontal" (two-column) |
Content Modes
- Full Content: Icon + text = standard layout
- Icon Only: Compact square sizing
- Text Only: Centered without reserved icon space
# File: docs/dash_flows/custom_icons.py
"""
Dash Flows - Custom Icons
=========================
Using DashIconify for custom node icons with flexible layouts.
"""
import dash_mantine_components as dmc
from dash import html
from dash_iconify import DashIconify
import dash_flows
# Nodes demonstrating icon features
nodes = [
# Full content with stacked layout (icon above text)
{
"id": "db-node",
"type": "input",
"data": {
"icon": DashIconify(icon="mdi:database", width=20, color="white"),
"label": "Database",
"body": "PostgreSQL",
"layout": "stacked",
},
"position": {"x": 50, "y": 50},
},
# Horizontal layout (icon left, text right)
{
"id": "process-node",
"type": "default",
"data": {
"icon": DashIconify(icon="mdi:cog", width=20, color="white"),
"label": "Transform",
"body": "Clean data",
"layout": "horizontal",
},
"position": {"x": 50, "y": 150},
},
# Icon-only node (compact)
{
"id": "icon-only",
"type": "default",
"data": {
"icon": DashIconify(icon="mdi:lightning-bolt", width=24, color="white"),
"iconColor": "#f59e0b",
"showIcon": True,
},
"position": {"x": 220, "y": 50},
},
# Text-only node (centered)
{
"id": "text-only",
"type": "default",
"data": {
"label": "Validate",
"sublabel": "Quality check",
"showIcon": False,
},
"position": {"x": 220, "y": 150},
},
# Output with horizontal layout
{
"id": "output-node",
"type": "output",
"data": {
"icon": DashIconify(icon="mdi:chart-bar", width=20, color="white"),
"label": "Dashboard",
"body": "Visualization",
"layout": "horizontal",
},
"position": {"x": 130, "y": 260},
},
]
edges = [
{"id": "e1", "source": "db-node", "target": "process-node", "animated": True},
{"id": "e2", "source": "icon-only", "target": "text-only", "animated": True},
]
component = dmc.Paper(
[
dmc.Text(
"Custom icons with DashIconify and flexible layout options.",
c="dimmed",
size="sm",
mb="md",
),
dmc.Group(
[
dmc.Badge("Stacked Layout", color="blue", variant="light", size="sm"),
dmc.Badge("Horizontal Layout", color="green", variant="light", size="sm"),
dmc.Badge("Icon Only", color="orange", variant="light", size="sm"),
dmc.Badge("Text Only", color="cyan", variant="light", size="sm"),
],
gap="xs",
mb="md",
),
dash_flows.DashFlows(
id="dash-flows-icons",
nodes=nodes,
edges=edges,
style={"height": "380px"},
fitView=True,
showControls=True,
showMiniMap=True,
),
dmc.Accordion(
[
dmc.AccordionItem(
[
dmc.AccordionControl("Icon Props Reference"),
dmc.AccordionPanel(
dmc.Table(
data={
"head": ["Prop", "Type", "Description"],
"body": [
["icon", "DashIconify", "Icon component to display"],
["iconColor", "str", "Background color for icon"],
["showIcon", "bool", "Toggle icon visibility"],
["layout", "str", "'stacked' or 'horizontal'"],
],
},
striped=True,
)
),
],
value="props",
),
dmc.AccordionItem(
[
dmc.AccordionControl("Content Modes"),
dmc.AccordionPanel(
dmc.List(
[
dmc.ListItem("Full Content: Icon + text = standard sizing"),
dmc.ListItem("Icon Only: Compact square (no label)"),
dmc.ListItem("Text Only: Centered without icon space"),
],
size="sm",
)
),
],
value="modes",
),
],
mt="md",
),
dmc.Alert(
[
dmc.Text("Browse icons at ", size="sm", span=True),
dmc.Anchor(
"icon-sets.iconify.design",
href="https://icon-sets.iconify.design/",
target="_blank",
size="sm",
),
],
color="gray",
mt="md",
),
],
p="md",
withBorder=True,
radius="md",
)
Node Data Props
Customize node content with these data properties:
| Prop | Type | Description |
|---|---|---|
label | str | Primary text (or title alias) |
sublabel | str | Secondary text below label |
body | str | Description text |
status | str | Visual state: "initial", "loading", "success", "error" |
multiline | bool | Enable text wrapping |
Status Indicators
# Loading state - blue pulsing glow
{"data": {"label": "Processing...", "status": "loading"}}
# Success state - green border with checkmark
{"data": {"label": "Complete", "status": "success"}}
# Error state - red border with X badge
{"data": {"label": "Failed", "status": "error"}}
Callbacks & Interactivity
Dash Flows provides rich callback support for handling user interactions:
Available Output Props
| Prop | Trigger | Data |
|---|---|---|
clickedNode | Single click | Node object |
doubleClickedNode | Double click | Node object |
contextMenuNode | Right-click | Node + position |
hoveredNode | Mouse enter/leave | Node ID or None |
selectedNodes | Selection change | List of node IDs |
selectedEdges | Selection change | List of edge IDs |
lastConnection | New connection | Source/target info |
deletedNodes | Node deletion | List of deleted IDs |
deletedEdges | Edge deletion | List of deleted IDs |
droppedNode | External drop | Drop position + data |
# File: docs/dash_flows/node_interactions.py
"""
Dash Flows - Node Interactions
==============================
Demonstrates callback support for node events.
"""
import dash_mantine_components as dmc
from dash import html, callback, Input, Output, State
import dash_flows
import json
# Initial nodes for interaction demo
initial_nodes = [
{"id": "1", "type": "default", "data": {"label": "Click Me"}, "position": {"x": 50, "y": 50}},
{"id": "2", "type": "default", "data": {"label": "Drag Me"}, "position": {"x": 200, "y": 50}},
{"id": "3", "type": "default", "data": {"label": "Right-Click"}, "position": {"x": 350, "y": 50}},
{"id": "4", "type": "default", "data": {"label": "Node 4"}, "position": {"x": 50, "y": 150}},
{"id": "5", "type": "default", "data": {"label": "Node 5"}, "position": {"x": 200, "y": 150}},
{"id": "6", "type": "default", "data": {"label": "Node 6"}, "position": {"x": 350, "y": 150}},
]
initial_edges = [
{"id": "e1-4", "source": "1", "target": "4"},
{"id": "e2-5", "source": "2", "target": "5"},
{"id": "e3-6", "source": "3", "target": "6"},
]
component = dmc.Paper(
[
dmc.Text(
"Interact with nodes and observe the callback events below.",
c="dimmed",
size="sm",
mb="md",
),
dmc.SimpleGrid(
cols={"base": 1, "md": 2},
spacing="md",
children=[
# Flow canvas
dmc.Paper(
dash_flows.DashFlows(
id="dash-flows-interactions",
nodes=initial_nodes,
edges=initial_edges,
style={"height": "300px"},
fitView=True,
showControls=True,
showMiniMap=False,
nodesDraggable=True,
nodesConnectable=True,
elementsSelectable=True,
multiSelectionKeyCode="Shift",
),
withBorder=True,
radius="md",
style={"overflow": "hidden"},
),
# Event panels
dmc.Stack(
[
dmc.Paper(
[
dmc.Text("Selected Nodes:", fw=600, size="sm"),
html.Pre(
id="dash-flows-selection-info",
children="Select nodes (Shift+click for multi)...",
style={"fontSize": "11px", "margin": 0, "maxHeight": "60px", "overflow": "auto"},
),
],
p="sm",
withBorder=True,
),
dmc.Paper(
[
dmc.Text("Context Menu (Right-Click):", fw=600, size="sm"),
html.Pre(
id="dash-flows-context-info",
children="Right-click a node...",
style={"fontSize": "11px", "margin": 0, "maxHeight": "60px", "overflow": "auto"},
),
],
p="sm",
withBorder=True,
),
dmc.Paper(
[
dmc.Text("Node Positions:", fw=600, size="sm"),
html.Pre(
id="dash-flows-positions-info",
children="Drag a node to see updates...",
style={"fontSize": "11px", "margin": 0, "maxHeight": "80px", "overflow": "auto"},
),
],
p="sm",
withBorder=True,
),
],
gap="xs",
),
],
),
dmc.Accordion(
[
dmc.AccordionItem(
[
dmc.AccordionControl("Available Callback Props"),
dmc.AccordionPanel(
dmc.Table(
data={
"head": ["Output Prop", "Trigger", "Data"],
"body": [
["clickedNode", "Single click", "Node object"],
["doubleClickedNode", "Double click", "Node object"],
["contextMenuNode", "Right-click", "Node + position"],
["hoveredNode", "Mouse enter/leave", "Node ID"],
["selectedNodes", "Selection change", "List of IDs"],
["selectedEdges", "Selection change", "List of IDs"],
["lastConnection", "New connection", "Source/target"],
["deletedNodes", "Deletion", "Deleted IDs"],
],
},
striped=True,
)
),
],
value="props",
),
],
mt="md",
),
dmc.Alert(
[
dmc.Text("Tips:", fw=500, size="sm"),
dmc.List(
[
dmc.ListItem("Hold Shift and click to multi-select nodes"),
dmc.ListItem("Drag on canvas to create selection box"),
dmc.ListItem("Use prevent_initial_call=True in callbacks"),
],
size="sm",
),
],
color="blue",
mt="md",
),
],
p="md",
withBorder=True,
radius="md",
)
# Callbacks for the interaction demo
@callback(
Output("dash-flows-selection-info", "children"),
Input("dash-flows-interactions", "selectedNodes"),
Input("dash-flows-interactions", "selectedEdges"),
)
def update_selection(nodes, edges):
"""Show currently selected nodes and edges."""
node_ids = []
if nodes:
for n in nodes:
if isinstance(n, dict) and "id" in n:
node_ids.append(n["id"])
elif isinstance(n, str):
node_ids.append(n)
edge_ids = []
if edges:
for e in edges:
if isinstance(e, dict) and "id" in e:
edge_ids.append(e["id"])
elif isinstance(e, str):
edge_ids.append(e)
return json.dumps({"nodes": node_ids, "edges": edge_ids}, indent=2)
@callback(
Output("dash-flows-context-info", "children"),
Input("dash-flows-interactions", "contextMenuNode"),
prevent_initial_call=True,
)
def update_context_menu(context_data):
"""Show context menu event data."""
if not context_data:
return "Right-click a node..."
return json.dumps(context_data, indent=2)
@callback(
Output("dash-flows-positions-info", "children"),
Input("dash-flows-interactions", "nodes"),
prevent_initial_call=True,
)
def update_positions(nodes):
"""Show node positions after dragging."""
if not nodes:
return "No nodes"
positions = {n["id"]: n["position"] for n in nodes[:3]} # Show first 3
return json.dumps(positions, indent=2)
Automatic Layouts (ELK)
Dash Flows integrates ELK.js for automatic graph layout. Pass layout options as a JSON string:
Layout Algorithms
| Algorithm | Best For |
|---|---|
layered | Hierarchical/directed graphs |
org.eclipse.elk.force | Organic, force-directed |
org.eclipse.elk.radial | Concentric circles |
org.eclipse.elk.stress | Balanced edge lengths |
Direction Options (Layered)
DOWN- Top to bottomUP- Bottom to topRIGHT- Left to rightLEFT- Right to left
import json
layout_options = json.dumps({
"elk.algorithm": "layered",
"elk.direction": "DOWN",
"elk.spacing.nodeNode": 50,
"elk.layered.spacing.nodeNodeBetweenLayers": 80,
})
dash_flows.DashFlows(
layoutOptions=layout_options,
# ... other props
)
# File: docs/dash_flows/elk_layouts.py
"""
Dash Flows - ELK Automatic Layouts
==================================
Demonstrates automatic graph layout algorithms using ELK.js.
"""
import dash_mantine_components as dmc
from dash import html, callback, Input, Output, State
import dash_flows
import json
# Create a graph to demonstrate layouts
initial_nodes = [
{"id": "1", "type": "input", "data": {"label": "Start"}, "position": {"x": 0, "y": 0}},
{"id": "2", "type": "default", "data": {"label": "Step A"}, "position": {"x": 0, "y": 0}},
{"id": "3", "type": "default", "data": {"label": "Step B"}, "position": {"x": 0, "y": 0}},
{"id": "4", "type": "default", "data": {"label": "Step C"}, "position": {"x": 0, "y": 0}},
{"id": "5", "type": "default", "data": {"label": "Step D"}, "position": {"x": 0, "y": 0}},
{"id": "6", "type": "default", "data": {"label": "Merge"}, "position": {"x": 0, "y": 0}},
{"id": "7", "type": "output", "data": {"label": "End"}, "position": {"x": 0, "y": 0}},
]
initial_edges = [
{"id": "e1-2", "source": "1", "target": "2"},
{"id": "e1-3", "source": "1", "target": "3"},
{"id": "e2-4", "source": "2", "target": "4"},
{"id": "e3-5", "source": "3", "target": "5"},
{"id": "e4-6", "source": "4", "target": "6"},
{"id": "e5-6", "source": "5", "target": "6"},
{"id": "e6-7", "source": "6", "target": "7"},
]
# Layout presets
layout_presets = {
"layered-down": {
"elk.algorithm": "layered",
"elk.direction": "DOWN",
"elk.spacing.nodeNode": 50,
"elk.layered.spacing.nodeNodeBetweenLayers": 80,
},
"layered-right": {
"elk.algorithm": "layered",
"elk.direction": "RIGHT",
"elk.spacing.nodeNode": 50,
"elk.layered.spacing.nodeNodeBetweenLayers": 120,
},
"force": {
"elk.algorithm": "org.eclipse.elk.force",
"elk.force.iterations": 300,
"elk.spacing.nodeNode": 80,
},
"radial": {
"elk.algorithm": "org.eclipse.elk.radial",
"elk.radial.radius": 150,
},
}
component = dmc.Paper(
[
dmc.Text(
"Apply automatic layout algorithms to arrange nodes.",
c="dimmed",
size="sm",
mb="md",
),
dmc.Group(
[
dmc.Select(
id="dash-flows-elk-layout-select",
label="Layout Algorithm",
data=[
{"value": "layered-down", "label": "Layered (Top to Bottom)"},
{"value": "layered-right", "label": "Layered (Left to Right)"},
{"value": "force", "label": "Force-Directed"},
{"value": "radial", "label": "Radial"},
],
value="layered-down",
style={"width": "220px"},
),
dmc.Button(
"Apply Layout",
id="dash-flows-elk-apply-btn",
variant="filled",
mt="auto",
),
],
gap="md",
mb="md",
),
dash_flows.DashFlows(
id="dash-flows-elk",
nodes=initial_nodes,
edges=initial_edges,
style={"height": "350px"},
fitView=True,
showControls=True,
showMiniMap=True,
layoutOptions=json.dumps(layout_presets["layered-down"]),
),
dmc.Accordion(
[
dmc.AccordionItem(
[
dmc.AccordionControl("Current Layout Options"),
dmc.AccordionPanel(
html.Pre(
id="dash-flows-elk-options-display",
children=json.dumps(layout_presets["layered-down"], indent=2),
style={"fontSize": "11px", "margin": 0},
)
),
],
value="options",
),
dmc.AccordionItem(
[
dmc.AccordionControl("Layout Algorithms"),
dmc.AccordionPanel(
dmc.Table(
data={
"head": ["Algorithm", "Best For"],
"body": [
["layered", "Hierarchical/directed graphs"],
["org.eclipse.elk.force", "Organic, force-directed"],
["org.eclipse.elk.radial", "Concentric circles"],
["org.eclipse.elk.stress", "Balanced edge lengths"],
],
},
striped=True,
)
),
],
value="algorithms",
),
],
value="options",
mt="md",
),
dmc.Alert(
[
dmc.Text("Important:", fw=500, size="sm"),
dmc.Text(
"layoutOptions must be a JSON string. Use json.dumps() to convert Python dicts.",
size="sm",
),
],
color="yellow",
mt="md",
),
],
p="md",
withBorder=True,
radius="md",
)
@callback(
Output("dash-flows-elk", "layoutOptions"),
Output("dash-flows-elk-options-display", "children"),
Input("dash-flows-elk-apply-btn", "n_clicks"),
State("dash-flows-elk-layout-select", "value"),
prevent_initial_call=True,
)
def apply_layout(n_clicks, layout_type):
"""Apply the selected layout algorithm."""
options = layout_presets.get(layout_type, layout_presets["layered-down"])
return json.dumps(options), json.dumps(options, indent=2)
Theming & Dark Mode
Customize the visual appearance with theme presets and color schemes:
Theme Presets
| Preset | Description |
|---|---|
glass | Glass morphism with blur effects (default) |
solid | Solid backgrounds |
minimal | Clean, minimal styling |
Color Schemes
default, ocean, forest, sunset, midnight, rose
Dark Mode
Integrates with Dash Mantine Components for dark mode support:
dmc.MantineProvider([
dash_flows.DashFlows(
colorMode="dark", # or "light" or "system"
# ... other props
)
])
# File: docs/dash_flows/theming_dark_mode.py
"""
Dash Flows - Theming & Dark Mode
================================
Demonstrates theme presets, color schemes, and dark mode integration.
"""
import dash_mantine_components as dmc
from dash import html, callback, Input, Output
import dash_flows
# Sample nodes for theming demo
nodes = [
{
"id": "input-1",
"type": "input",
"data": {"label": "Data Source", "sublabel": "Input"},
"position": {"x": 50, "y": 50},
},
{
"id": "process-1",
"type": "default",
"data": {"label": "Transform", "sublabel": "Process"},
"position": {"x": 50, "y": 150},
},
{
"id": "process-2",
"type": "default",
"data": {"label": "Validate", "sublabel": "Check"},
"position": {"x": 200, "y": 150},
},
{
"id": "output-1",
"type": "output",
"data": {"label": "Dashboard", "sublabel": "Output"},
"position": {"x": 125, "y": 250},
},
]
edges = [
{"id": "e1", "source": "input-1", "target": "process-1", "animated": True},
{"id": "e2", "source": "input-1", "target": "process-2", "animated": True},
{"id": "e3", "source": "process-1", "target": "output-1"},
{"id": "e4", "source": "process-2", "target": "output-1"},
]
component = dmc.Paper(
[
dmc.Text(
"Customize appearance with theme presets and color schemes.",
c="dimmed",
size="sm",
mb="md",
),
dmc.SimpleGrid(
cols={"base": 1, "sm": 3},
spacing="md",
mb="md",
children=[
dmc.Select(
id="dash-flows-theme-preset",
label="Theme Preset",
data=[
{"value": "glass", "label": "Glass (Default)"},
{"value": "solid", "label": "Solid"},
{"value": "minimal", "label": "Minimal"},
],
value="glass",
),
dmc.Select(
id="dash-flows-color-scheme",
label="Color Scheme",
data=[
{"value": "default", "label": "Default"},
{"value": "ocean", "label": "Ocean"},
{"value": "forest", "label": "Forest"},
{"value": "sunset", "label": "Sunset"},
{"value": "midnight", "label": "Midnight"},
{"value": "rose", "label": "Rose"},
],
value="default",
),
dmc.Select(
id="dash-flows-color-mode",
label="Color Mode",
data=[
{"value": "light", "label": "Light"},
{"value": "dark", "label": "Dark"},
],
value="light",
),
],
),
dash_flows.DashFlows(
id="dash-flows-theming",
nodes=nodes,
edges=edges,
style={"height": "350px"},
fitView=True,
showControls=True,
showMiniMap=True,
themePreset="glass",
colorScheme="default",
colorMode="light",
),
dmc.Accordion(
[
dmc.AccordionItem(
[
dmc.AccordionControl("Theme Presets"),
dmc.AccordionPanel(
dmc.Table(
data={
"head": ["Preset", "Description"],
"body": [
["glass", "Glass morphism with blur effects"],
["solid", "Solid, opaque backgrounds"],
["minimal", "Clean, minimal styling"],
],
},
striped=True,
)
),
],
value="presets",
),
dmc.AccordionItem(
[
dmc.AccordionControl("Color Schemes"),
dmc.AccordionPanel(
dmc.Group(
[
dmc.Badge("default", color="blue", variant="filled"),
dmc.Badge("ocean", color="cyan", variant="filled"),
dmc.Badge("forest", color="green", variant="filled"),
dmc.Badge("sunset", color="orange", variant="filled"),
dmc.Badge("midnight", color="indigo", variant="filled"),
dmc.Badge("rose", color="pink", variant="filled"),
],
gap="xs",
)
),
],
value="schemes",
),
],
mt="md",
),
dmc.Alert(
[
dmc.Text("Dark Mode Integration:", fw=500, size="sm"),
dmc.Text(
"For full dark mode support, wrap your app in dmc.MantineProvider "
"and use colorMode='dark' on DashFlows.",
size="sm",
),
],
color="gray",
mt="md",
),
],
p="md",
withBorder=True,
radius="md",
)
@callback(
Output("dash-flows-theming", "themePreset"),
Output("dash-flows-theming", "colorScheme"),
Output("dash-flows-theming", "colorMode"),
Input("dash-flows-theme-preset", "value"),
Input("dash-flows-color-scheme", "value"),
Input("dash-flows-color-mode", "value"),
)
def update_theme(preset, scheme, mode):
"""Update the flow theme based on selections."""
return preset or "glass", scheme or "default", mode or "light"
Controls & Display
Built-in UI Components
| Prop | Default | Description |
|---|---|---|
showControls | True | Zoom +/- and fit view buttons |
showMiniMap | True | Overview minimap |
showBackground | True | Canvas background pattern |
showDevTools | False | Debug information panel |
Background Patterns
dash_flows.DashFlows(
showBackground=True,
backgroundVariant="dots", # "dots", "lines", or "cross"
backgroundGap=16, # Pattern spacing
)
Control Positions
controlsPosition="bottom-left" # Default
miniMapPosition="bottom-right" # Default
Viewport Control
Programmatically control the viewport:
# Fit all nodes in view
viewportAction={"type": "fitView", "options": {"padding": 0.2}}
# Zoom in/out
viewportAction={"type": "zoomIn"}
viewportAction={"type": "zoomOut"}
# Center on specific coordinates
viewportAction={"type": "setCenter", "x": 200, "y": 150, "zoom": 1.5}
Viewport Props
| Prop | Default | Description |
|---|---|---|
minZoom | 0.5 | Minimum zoom level |
maxZoom | 2 | Maximum zoom level |
fitView | False | Auto-fit on initial render |
snapToGrid | False | Snap nodes to grid |
snapGrid | [15, 15] | Grid size for snapping |
Interaction Props
Control how users interact with the flow:
| Prop | Default | Description |
|---|---|---|
nodesDraggable | True | Allow dragging nodes |
nodesConnectable | True | Allow creating connections |
elementsSelectable | True | Allow selecting elements |
panOnDrag | True | Pan canvas by dragging |
zoomOnScroll | True | Zoom with scroll wheel |
zoomOnPinch | True | Zoom with pinch gesture |
zoomOnDoubleClick | True | Zoom on double-click |
multiSelectionKeyCode | "Shift" | Key for multi-select |
deleteKeyCode | "Backspace" | Key to delete selected |
Props Reference
Core Props
| Prop | Type | Default | Description |
|---|---|---|---|
id | str | Required | Component ID for callbacks |
nodes | list | [] | Array of node objects |
edges | list | [] | Array of edge objects |
style | dict | {} | Container style (height required!) |
Display Props
| Prop | Type | Default | Description |
|---|---|---|---|
fitView | bool | False | Auto-fit on mount |
showControls | bool | True | Show viewport controls |
showMiniMap | bool | True | Show minimap |
showBackground | bool | True | Show background pattern |
backgroundVariant | str | "dots" | "dots", "lines", "cross" |
controlsPosition | str | "bottom-left" | Control panel position |
miniMapPosition | str | "bottom-right" | Minimap position |
Theme Props
| Prop | Type | Default | Description |
|---|---|---|---|
themePreset | str | "glass" | "glass", "solid", "minimal" |
colorScheme | str | "default" | Color scheme name |
colorMode | str | "light" | "light", "dark", "system" |
Layout Props
| Prop | Type | Default | Description |
|---|---|---|---|
layoutOptions | str | None | ELK layout JSON string |
defaultViewport | dict | None | Initial viewport {x, y, zoom} |
minZoom | float | 0.5 | Minimum zoom level |
maxZoom | float | 2 | Maximum zoom level |
Callback Output Props
| Prop | Type | Description |
|---|---|---|
selectedNodes | list | Currently selected node IDs |
selectedEdges | list | Currently selected edge IDs |
clickedNode | dict | Last clicked node |
doubleClickedNode | dict | Last double-clicked node |
contextMenuNode | dict | Right-clicked node with position |
hoveredNode | dict | Currently hovered node |
lastConnection | dict | Last created connection |
deletedNodes | list | Recently deleted node IDs |
deletedEdges | list | Recently deleted edge IDs |
viewport | dict | Current viewport state |
New in 1.2.0
Smart Handle Positioning
# File: docs/dash_flows/smart_handles.py
"""Smart Handle Positioning — auto-routes edges to closest node side."""
import dash_mantine_components as dmc
import dash_flows
from dash_iconify import DashIconify
nodes = [
{"id": "a", "type": "input", "data": {"label": "Data Source"}, "position": {"x": 50, "y": 50}},
{"id": "b", "type": "default", "data": {"label": "Transform"}, "position": {"x": 300, "y": 50}},
{"id": "c", "type": "default", "data": {"label": "Validate"}, "position": {"x": 150, "y": 200}},
{"id": "d", "type": "output", "data": {"label": "Output"}, "position": {"x": 400, "y": 200}},
{"id": "e", "type": "default", "data": {"label": "Cache"}, "position": {"x": 50, "y": 300}},
]
edges = [
{"id": "e1", "source": "a", "target": "b", "animated": True},
{"id": "e2", "source": "a", "target": "c", "animated": True},
{"id": "e3", "source": "b", "target": "d", "animated": True},
{"id": "e4", "source": "c", "target": "d", "animated": True},
{"id": "e5", "source": "c", "target": "e", "animated": True},
]
component = dmc.Paper([
dmc.Group([
dmc.Text("Smart Handle Positioning", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text("Edges automatically route to the closest side of each node. Drag nodes around to see handles reposition.", size="sm", c="dimmed", mb="md"),
dash_flows.DashFlows(
id="df-smart-handles",
nodes=nodes,
edges=edges,
smartHandles=True,
style={"height": "400px"},
fitView=True,
showControls=True,
showMiniMap=True,
),
], p="md", withBorder=True, radius="md")
Floating Edges
# File: docs/dash_flows/floating_edges.py
"""Floating Edges — connect to nearest point on node border."""
import dash_mantine_components as dmc
import dash_flows
nodes = [
{"id": "1", "type": "input", "data": {"label": "API Gateway"}, "position": {"x": 0, "y": 100}},
{"id": "2", "type": "default", "data": {"label": "Auth Service"}, "position": {"x": 250, "y": 0}},
{"id": "3", "type": "default", "data": {"label": "User Service"}, "position": {"x": 250, "y": 200}},
{"id": "4", "type": "default", "data": {"label": "Database"}, "position": {"x": 500, "y": 100}},
{"id": "5", "type": "output", "data": {"label": "Response"}, "position": {"x": 700, "y": 100}},
]
edges = [
{"id": "e1", "source": "1", "target": "2", "type": "floating", "animated": True},
{"id": "e2", "source": "1", "target": "3", "type": "floating", "animated": True},
{"id": "e3", "source": "2", "target": "4", "type": "floating"},
{"id": "e4", "source": "3", "target": "4", "type": "floating"},
{"id": "e5", "source": "4", "target": "5", "type": "floating", "animated": True},
]
component = dmc.Paper([
dmc.Group([
dmc.Text("Floating Edges", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text(
"Edges connect to the nearest point on each node's border instead of fixed handle positions. "
"Drag nodes to see edges dynamically recalculate their connection points.",
size="sm", c="dimmed", mb="md",
),
dash_flows.DashFlows(
id="df-floating-edges",
nodes=nodes,
edges=edges,
style={"height": "380px"},
fitView=True,
showControls=True,
),
dmc.Code('{"type": "floating"} # Set on any edge to enable border intersection', block=True),
], p="md", withBorder=True, radius="md")
Helper Lines (Alignment Guides)
# File: docs/dash_flows/helper_lines.py
"""Helper Lines — alignment guides when dragging nodes."""
import dash_mantine_components as dmc
import dash_flows
nodes = [
{"id": "1", "type": "input", "data": {"label": "Node A"}, "position": {"x": 50, "y": 50}},
{"id": "2", "type": "default", "data": {"label": "Node B"}, "position": {"x": 250, "y": 50}},
{"id": "3", "type": "default", "data": {"label": "Node C"}, "position": {"x": 150, "y": 180}},
{"id": "4", "type": "output", "data": {"label": "Node D"}, "position": {"x": 350, "y": 180}},
]
edges = [
{"id": "e1", "source": "1", "target": "3"},
{"id": "e2", "source": "2", "target": "3"},
{"id": "e3", "source": "3", "target": "4"},
]
component = dmc.Paper([
dmc.Group([
dmc.Text("Helper Lines (Alignment Guides)", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text(
"Blue alignment guides appear when dragging nodes near other nodes' edges. "
"Nodes snap to alignment within the threshold distance.",
size="sm", c="dimmed", mb="md",
),
dash_flows.DashFlows(
id="df-helper-lines",
nodes=nodes,
edges=edges,
helperLines=True,
helperLineThreshold=5,
style={"height": "350px"},
fitView=True,
showControls=True,
),
dmc.Alert(
"Drag any node slowly near another to see the blue alignment guides appear and snap.",
color="blue", variant="light", mt="md",
),
], p="md", withBorder=True, radius="md")
Sub-flows (Collapsible Groups)
# File: docs/dash_flows/subflows.py
"""Sub-flows — collapsible group nodes."""
import dash_mantine_components as dmc
import dash_flows
from dash import callback, Input, Output, no_update
nodes = [
{"id": "source", "type": "input", "data": {"label": "Data Source"}, "position": {"x": 50, "y": 130}},
# Group container
{
"id": "pipeline",
"type": "group",
"data": {"label": "ETL Pipeline", "collapsedWidth": 200, "collapsedHeight": 52},
"position": {"x": 250, "y": 50},
"style": {"width": 350, "height": 250},
},
# Children inside group
{"id": "extract", "type": "default", "data": {"label": "Extract"}, "position": {"x": 30, "y": 40}, "parentId": "pipeline", "extent": "parent"},
{"id": "transform", "type": "default", "data": {"label": "Transform"}, "position": {"x": 30, "y": 130}, "parentId": "pipeline", "extent": "parent"},
{"id": "load", "type": "default", "data": {"label": "Load"}, "position": {"x": 180, "y": 85}, "parentId": "pipeline", "extent": "parent"},
# Output
{"id": "dashboard", "type": "output", "data": {"label": "Dashboard"}, "position": {"x": 700, "y": 130}},
]
edges = [
{"id": "e1", "source": "source", "target": "extract", "animated": True},
{"id": "e2", "source": "extract", "target": "transform"},
{"id": "e3", "source": "transform", "target": "load"},
{"id": "e4", "source": "load", "target": "dashboard", "animated": True},
]
@callback(
Output("df-subflows", "toggleCollapseNode"),
Input("df-subflows", "doubleClickedNode"),
prevent_initial_call=True,
)
def toggle_group_collapse(double_clicked):
if double_clicked and double_clicked.get("type") == "group":
return double_clicked["id"]
return no_update
component = dmc.Paper([
dmc.Group([
dmc.Text("Sub-flows (Collapsible Groups)", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text(
"Double-click the 'ETL Pipeline' group to collapse/expand it. "
"Child nodes hide when collapsed; external edges remain connected at the group level.",
size="sm", c="dimmed", mb="md",
),
dash_flows.DashFlows(
id="df-subflows",
nodes=nodes,
edges=edges,
style={"height": "380px"},
fitView=True,
showControls=True,
showMiniMap=True,
),
dmc.Alert(
"Group nodes support collapsedWidth/collapsedHeight for compact dimensions when collapsed.",
color="blue", variant="light", mt="md",
),
], p="md", withBorder=True, radius="md")
Undo / Redo
# File: docs/dash_flows/undo_redo.py
"""Undo/Redo — history tracking for node/edge changes."""
import dash_mantine_components as dmc
from dash import html, callback, Input, Output, State
from dash_iconify import DashIconify
import dash_flows
nodes = [
{"id": "a", "type": "input", "data": {"label": "Input A"}, "position": {"x": 50, "y": 50}},
{"id": "b", "type": "default", "data": {"label": "Process"}, "position": {"x": 250, "y": 50}},
{"id": "c", "type": "output", "data": {"label": "Output"}, "position": {"x": 450, "y": 50}},
]
edges = [
{"id": "e1", "source": "a", "target": "b", "animated": True},
{"id": "e2", "source": "b", "target": "c", "animated": True},
]
component = dmc.Paper([
dmc.Group([
dmc.Text("Undo / Redo", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text(
"Drag nodes, create connections, or delete elements — then undo/redo with the buttons below.",
size="sm", c="dimmed", mb="md",
),
dmc.Group([
dmc.Button("Undo", id="df-undo-btn", leftSection=DashIconify(icon="tabler:arrow-back-up", width=16), variant="light", size="sm"),
dmc.Button("Redo", id="df-redo-btn", leftSection=DashIconify(icon="tabler:arrow-forward-up", width=16), variant="light", size="sm"),
html.Div(id="df-undo-status"),
], mb="md"),
dash_flows.DashFlows(
id="df-undo-redo",
nodes=nodes,
edges=edges,
enableUndoRedo=True,
undoRedoMaxHistory=30,
style={"height": "300px"},
fitView=True,
showControls=True,
),
dmc.Alert(
"Try: drag a node, then click Undo to reverse. Tracks position changes, additions, and deletions.",
color="blue", variant="light", mt="md",
),
], p="md", withBorder=True, radius="md")
@callback(
Output("df-undo-redo", "undoRedoAction"),
Input("df-undo-btn", "n_clicks"),
Input("df-redo-btn", "n_clicks"),
prevent_initial_call=True,
)
def handle_undo_redo(undo_clicks, redo_clicks):
from dash import ctx
if ctx.triggered_id == "df-undo-btn":
return {"action": "undo"}
return {"action": "redo"}
@callback(
Output("df-undo-status", "children"),
Input("df-undo-redo", "undoRedoState"),
prevent_initial_call=True,
)
def show_undo_state(state):
if not state:
return ""
return dmc.Group([
dmc.Badge(f"Undo: {state.get('undoCount', 0)}", color="gray", variant="light", size="sm"),
dmc.Badge(f"Redo: {state.get('redoCount', 0)}", color="gray", variant="light", size="sm"),
], gap="xs")
Additional 1.2.0 Features
The following features are also available — view source code for implementation details:
Add Node on Edge Drop — Drag a connection to empty canvas to create a node:
# File: docs/dash_flows/add_node_drop.py
"""Add Node on Edge Drop — drag a connection to empty canvas to create a node."""
import dash_mantine_components as dmc
import dash_flows
nodes = [
{"id": "start", "type": "input", "data": {"label": "Start"}, "position": {"x": 50, "y": 100}},
{"id": "step-1", "type": "default", "data": {"label": "Step 1"}, "position": {"x": 300, "y": 100}},
]
edges = [
{"id": "e1", "source": "start", "target": "step-1", "animated": True},
]
component = dmc.Paper([
dmc.Group([
dmc.Text("Add Node on Edge Drop", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text(
"Drag a connection from any handle and drop it on empty canvas to create a new node. "
"The new node is automatically connected to the source.",
size="sm", c="dimmed", mb="md",
),
dash_flows.DashFlows(
id="df-add-node-drop",
nodes=nodes,
edges=edges,
addNodeOnEdgeDrop=True,
style={"height": "350px"},
fitView=True,
showControls=True,
),
dmc.Alert(
"Drag from the bottom handle of any node into empty space, then release to create a new node.",
color="indigo", variant="light", mt="md",
),
], p="md", withBorder=True, radius="md")
Animated Layout Transitions — Smooth ELK layout changes with callbacks:
# File: docs/dash_flows/animated_layout.py
"""Animated Layout Transitions — smooth ELK layout changes."""
import json
import dash_mantine_components as dmc
from dash import callback, Input, Output
import dash_flows
nodes = [
{"id": "1", "type": "input", "data": {"label": "Source"}, "position": {"x": 0, "y": 0}},
{"id": "2", "type": "default", "data": {"label": "Parse"}, "position": {"x": 200, "y": 0}},
{"id": "3", "type": "default", "data": {"label": "Transform"}, "position": {"x": 100, "y": 120}},
{"id": "4", "type": "default", "data": {"label": "Validate"}, "position": {"x": 300, "y": 120}},
{"id": "5", "type": "default", "data": {"label": "Enrich"}, "position": {"x": 200, "y": 240}},
{"id": "6", "type": "output", "data": {"label": "Output"}, "position": {"x": 200, "y": 360}},
]
edges = [
{"id": "e1", "source": "1", "target": "2"},
{"id": "e2", "source": "1", "target": "3"},
{"id": "e3", "source": "2", "target": "4"},
{"id": "e4", "source": "3", "target": "5"},
{"id": "e5", "source": "4", "target": "5"},
{"id": "e6", "source": "5", "target": "6", "animated": True},
]
LAYOUTS = {
"layered-down": json.dumps({"elk.algorithm": "layered", "elk.direction": "DOWN", "elk.spacing.nodeNode": "60"}),
"layered-right": json.dumps({"elk.algorithm": "layered", "elk.direction": "RIGHT", "elk.spacing.nodeNode": "60"}),
"force": json.dumps({"elk.algorithm": "org.eclipse.elk.force", "elk.spacing.nodeNode": "80"}),
"radial": json.dumps({"elk.algorithm": "org.eclipse.elk.radial", "elk.spacing.nodeNode": "80"}),
}
component = dmc.Paper([
dmc.Group([
dmc.Text("Animated Layout Transitions", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text("Nodes animate smoothly between positions when switching ELK layouts.", size="sm", c="dimmed", mb="md"),
dmc.Group([
dmc.Select(
id="df-anim-layout-select",
data=[
{"value": "layered-down", "label": "Layered (Down)"},
{"value": "layered-right", "label": "Layered (Right)"},
{"value": "force", "label": "Force-Directed"},
{"value": "radial", "label": "Radial"},
],
value="layered-down",
w=200,
),
], mb="md"),
dash_flows.DashFlows(
id="df-animated-layout",
nodes=nodes,
edges=edges,
animateLayout=True,
animateLayoutDuration=500,
style={"height": "400px"},
fitView=True,
showControls=True,
showMiniMap=True,
layoutOptions=LAYOUTS["layered-down"],
),
], p="md", withBorder=True, radius="md")
@callback(
Output("df-animated-layout", "layoutOptions"),
Input("df-anim-layout-select", "value"),
prevent_initial_call=True,
)
def switch_df_layout(layout_key):
from dash import no_update
if not layout_key:
return no_update
return LAYOUTS.get(layout_key, LAYOUTS["layered-down"])
Computing Flows — Topological sort and data propagation:
# File: docs/dash_flows/computing_flows.py
"""Computing Flows — topological sort and data propagation."""
import dash_mantine_components as dmc
from dash import callback, Input, Output
from dash_iconify import DashIconify
import dash_flows
import json
nodes = [
{"id": "input-a", "type": "input", "data": {"label": "Input A", "computedValue": 10}, "position": {"x": 0, "y": 0}},
{"id": "input-b", "type": "input", "data": {"label": "Input B", "computedValue": 5}, "position": {"x": 0, "y": 150}},
{"id": "add", "type": "default", "data": {"label": "Add"}, "position": {"x": 250, "y": 75}},
{"id": "multiply", "type": "default", "data": {"label": "Multiply ×2"}, "position": {"x": 500, "y": 75}},
{"id": "result", "type": "output", "data": {"label": "Result"}, "position": {"x": 750, "y": 75}},
]
edges = [
{"id": "e1", "source": "input-a", "target": "add", "animated": True},
{"id": "e2", "source": "input-b", "target": "add", "animated": True},
{"id": "e3", "source": "add", "target": "multiply", "animated": True},
{"id": "e4", "source": "multiply", "target": "result", "animated": True},
]
component = dmc.Paper([
dmc.Group([
dmc.Text("Computing Flows", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text(
"Topological sort determines execution order. JS handles graph traversal; Python handles computation.",
size="sm", c="dimmed", mb="md",
),
dmc.Group([
dmc.Button("Compute Flow", id="df-compute-btn", leftSection=DashIconify(icon="tabler:player-play", width=16), color="green", size="sm"),
], mb="md"),
dash_flows.DashFlows(
id="df-computing-flow",
nodes=nodes,
edges=edges,
style={"height": "280px"},
fitView=True,
showControls=True,
),
dmc.Code(id="df-compute-result", children="Click 'Compute Flow' to run topological sort", block=True),
], p="md", withBorder=True, radius="md")
@callback(
Output("df-computing-flow", "computeAction"),
Input("df-compute-btn", "n_clicks"),
prevent_initial_call=True,
)
def trigger_compute(_n):
return {"action": "compute"}
@callback(
Output("df-compute-result", "children"),
Input("df-computing-flow", "computeResult"),
prevent_initial_call=True,
)
def show_result(result):
if not result:
return "No result yet"
return json.dumps(result, indent=2)
Resize Constraints — Aspect ratio lock and min/max dimensions:
# File: docs/dash_flows/resize_constraints.py
"""Resize Constraints — aspect ratio, min/max dimensions."""
import dash_mantine_components as dmc
import dash_flows
nodes = [
{
"id": "free", "type": "resizable",
"data": {"label": "Free Resize", "sublabel": "No constraints"},
"position": {"x": 50, "y": 50},
"style": {"width": 180, "height": 100},
},
{
"id": "aspect", "type": "resizable",
"data": {"label": "Locked Aspect", "sublabel": "keepAspectRatio: true", "keepAspectRatio": True},
"position": {"x": 300, "y": 50},
"style": {"width": 180, "height": 120},
},
{
"id": "constrained", "type": "resizable",
"data": {
"label": "Min/Max Limits",
"sublabel": "100-400px wide, 80-200px tall",
"minWidth": 100, "minHeight": 80,
"maxWidth": 400, "maxHeight": 200,
},
"position": {"x": 550, "y": 50},
"style": {"width": 200, "height": 120},
},
]
edges = [
{"id": "e1", "source": "free", "target": "aspect"},
{"id": "e2", "source": "aspect", "target": "constrained"},
]
component = dmc.Paper([
dmc.Group([
dmc.Text("Resize Constraints", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text("Drag the resize handles on each node to see different constraint behaviors.", size="sm", c="dimmed", mb="md"),
dash_flows.DashFlows(
id="df-resize-constraints",
nodes=nodes,
edges=edges,
style={"height": "300px"},
fitView=True,
showControls=True,
),
dmc.SimpleGrid(cols=3, mt="md", children=[
dmc.Badge("Free: no limits", color="gray", variant="light", fullWidth=True),
dmc.Badge("Aspect: ratio locked", color="blue", variant="light", fullWidth=True),
dmc.Badge("Min/Max: bounded size", color="orange", variant="light", fullWidth=True),
]),
], p="md", withBorder=True, radius="md")
Accessibility (ARIA) — Screen reader labels and keyboard navigation:
# File: docs/dash_flows/accessibility.py
"""Accessibility — ARIA labels and keyboard navigation."""
import dash_mantine_components as dmc
import dash_flows
nodes = [
{"id": "start", "type": "input", "data": {"label": "Start"}, "position": {"x": 50, "y": 50}, "ariaLabel": "Start node — entry point of the workflow"},
{"id": "validate", "type": "default", "data": {"label": "Validate"}, "position": {"x": 250, "y": 50}, "ariaLabel": "Validation step — checks input data integrity"},
{"id": "process", "type": "default", "data": {"label": "Process"}, "position": {"x": 450, "y": 50}, "ariaLabel": "Processing step — transforms validated data"},
{"id": "end", "type": "output", "data": {"label": "Complete"}, "position": {"x": 650, "y": 50}, "ariaLabel": "Completion node — workflow output"},
]
edges = [
{"id": "e1", "source": "start", "target": "validate", "ariaLabel": "Flow from start to validation"},
{"id": "e2", "source": "validate", "target": "process", "ariaLabel": "Flow from validation to processing"},
{"id": "e3", "source": "process", "target": "end", "ariaLabel": "Flow from processing to completion"},
]
component = dmc.Paper([
dmc.Group([
dmc.Text("Accessibility (ARIA Support)", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text("Full ARIA labels for screen readers. Tab through nodes, use arrow keys to pan.", size="sm", c="dimmed", mb="md"),
dash_flows.DashFlows(
id="df-accessibility",
nodes=nodes,
edges=edges,
nodesFocusable=True,
edgesFocusable=True,
ariaLabelConfig={
"rfDiagram": "Interactive data processing workflow",
"miniMap": "Minimap navigation panel",
"controls": "Zoom and pan controls",
},
style={"height": "280px"},
fitView=True,
showControls=True,
showMiniMap=True,
),
dmc.Alert(
dmc.Stack([
dmc.Text("Keyboard Navigation:", size="sm", fw=500),
dmc.List([
dmc.ListItem("Tab — cycle through nodes and edges"),
dmc.ListItem("Enter — select focused element"),
dmc.ListItem("Delete/Backspace — remove selected"),
dmc.ListItem("Arrow keys — pan the viewport"),
], size="sm"),
], gap="xs"),
color="indigo", variant="light", mt="md",
),
], p="md", withBorder=True, radius="md")
Viewport Portal — Floating annotations at flow coordinates:
# File: docs/dash_flows/viewport_portal.py
"""Viewport Portal — floating annotations at flow coordinates."""
import dash_mantine_components as dmc
import dash_flows
nodes = [
{"id": "api", "type": "input", "data": {"label": "API Layer"}, "position": {"x": 100, "y": 100}},
{"id": "cache", "type": "default", "data": {"label": "Cache"}, "position": {"x": 350, "y": 50}},
{"id": "db", "type": "default", "data": {"label": "Database"}, "position": {"x": 350, "y": 180}},
{"id": "response", "type": "output", "data": {"label": "Response"}, "position": {"x": 600, "y": 100}},
]
edges = [
{"id": "e1", "source": "api", "target": "cache"},
{"id": "e2", "source": "api", "target": "db"},
{"id": "e3", "source": "cache", "target": "response"},
{"id": "e4", "source": "db", "target": "response"},
]
overlays = [
{"x": 80, "y": 30, "content": "Incoming Traffic", "style": {"background": "rgba(59,130,246,0.1)", "color": "#3b82f6", "padding": "4px 10px", "borderRadius": "6px", "fontSize": "12px", "fontWeight": "600", "border": "1px solid rgba(59,130,246,0.3)"}},
{"x": 320, "y": -10, "content": "Hot Path (< 5ms)", "style": {"background": "rgba(34,197,94,0.1)", "color": "#22c55e", "padding": "4px 10px", "borderRadius": "6px", "fontSize": "11px", "border": "1px solid rgba(34,197,94,0.3)"}},
{"x": 320, "y": 250, "content": "Cold Path (50-200ms)", "style": {"background": "rgba(249,115,22,0.1)", "color": "#f97316", "padding": "4px 10px", "borderRadius": "6px", "fontSize": "11px", "border": "1px solid rgba(249,115,22,0.3)"}},
{"x": 580, "y": 30, "content": "Output", "style": {"background": "rgba(168,85,247,0.1)", "color": "#a855f7", "padding": "4px 10px", "borderRadius": "6px", "fontSize": "12px", "fontWeight": "600", "border": "1px solid rgba(168,85,247,0.3)"}},
]
component = dmc.Paper([
dmc.Group([
dmc.Text("Viewport Portal (Floating Annotations)", fw=600),
dmc.Badge("1.2.0", color="teal", variant="light", size="sm"),
], mb="xs"),
dmc.Text(
"Floating overlays anchored to flow coordinates that move with pan and zoom. "
"Use for annotations, labels, region markers, and performance indicators.",
size="sm", c="dimmed", mb="md",
),
dash_flows.DashFlows(
id="df-viewport-portal",
nodes=nodes,
edges=edges,
viewportOverlays=overlays,
style={"height": "350px"},
fitView=True,
showControls=True,
),
dmc.Code(
'''viewportOverlays=[
{"x": 100, "y": 50, "content": "Label", "style": {"background": "...", "color": "..."}},
]''',
block=True,
),
], p="md", withBorder=True, radius="md")
1.2.0 Props Reference
| Property | Type | Default | Description |
|---|---|---|---|
smartHandles | bool | False | Auto-route edges to closest node side |
helperLines | bool | False | Show alignment guides when dragging |
helperLineThreshold | number | 5 | Snap distance in pixels |
addNodeOnEdgeDrop | bool | False | Create node by dropping edge on canvas |
animateLayout | bool | False | Smooth transitions between layouts |
animateLayoutDuration | number | 300 | Animation duration (ms) |
enableUndoRedo | bool | False | Enable history tracking |
undoRedoMaxHistory | number | 50 | Max history snapshots |
undoRedoAction | dict | None | Trigger undo/redo: {action: 'undo'} |
undoRedoState | dict | Output | {canUndo, canRedo, undoCount, redoCount} |
computeAction | dict | None | Trigger computation: {action: 'compute'} |
computeResult | dict | Output | {traversalOrder, nodeInputs, timestamp} |
toggleCollapseNode | string | None | Group node ID to toggle collapse |
collapsedGroups | list | Output | Currently collapsed group IDs |
viewportOverlays | list | None | Floating annotations [{x, y, content, style}] |
connectionDragThreshold | number | 1 | Min drag distance before connection |
zIndexMode | string | "default" | "default" or "elevate" |
autoPanOnNodeFocus | bool | False | Pan when Tab-focusing nodes |
nodesFocusable | bool | False | Enable Tab navigation |
edgesFocusable | bool | False | Enable Tab for edges |
ariaLabelConfig | dict | None | ARIA labels for regions |
deleteElementsAction | dict | None | Programmatic deletion |
edgeDroppedNode | dict | Output | Node created from dropped edge |
nodeConnections | dict | Output | Real-time connection map |
Troubleshooting
Nodes Not Appearing
- Ensure each node has
id,type,data, andposition - Check that container has explicit height:
style={"height": "500px"} - Verify node IDs are unique strings
Edges Not Connecting
- Verify
sourceandtargetmatch existing node IDs - Check that source node has a source handle (not output type)
- Check that target node has a target handle (not input type)
Callbacks Not Firing
- Add
prevent_initial_call=Trueto avoid initial triggers - Ensure component ID matches callback Input/Output
- Check browser console for JavaScript errors
Layout Not Applying
- Ensure
layoutOptionsis a valid JSON string viajson.dumps() - Verify ELK algorithm name is correct
- Try adding explicit spacing options
Multi-Select Not Working
- Set
multiSelectionKeyCode="Shift"explicitly - Ensure
elementsSelectable=True
Dark Mode Issues
- Use modern DMC v2.4.0+ pattern
- Wrap in
dmc.MantineProvider - Set
colorMode="dark"on DashFlows
Resources
- React Flow Documentation - Underlying library
- Iconify Icon Search - Browse icons
- ELK Layout Options - Layout configuration
- Dash Mantine Components - UI components