# Dash Flows > Interactive node-based flow diagrams with React Flow integration for Plotly Dash --- .. toc:: .. llms_copy::Dash Flows ### 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 ```bash pip install dash-flows ``` **Optional dependencies for enhanced features:** ```bash 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`, and `position` - Define **edges** with `id`, `source`, and `target` - Set an explicit **height** on the component .. exec::docs.dash_flows.introduction :code: false ```python # 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: ```python { "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: ```python { "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 } ``` .. exec::docs.dash_flows.basic_nodes_edges :code: false ```python # 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 | .. exec::docs.dash_flows.all_node_types :code: false ```python # File: docs/dash_flows/all_node_types.py """ Dash Flows - All Node Types =========================== Showcases all 6 available node types in Dash Flows. """ import dash_mantine_components as dmc from dash import html import dash_flows nodes = [ # Input Node - Entry point (green accent, source handle only) { "id": "input-1", "type": "input", "data": { "label": "Input Node", "sublabel": "Entry point", }, "position": {"x": 50, "y": 50}, }, # Default Node - Standard processing (both handles) { "id": "default-1", "type": "default", "data": { "label": "Default Node", "sublabel": "Both handles", }, "position": {"x": 50, "y": 150}, }, # Output Node - Exit point (purple accent, target handle only) { "id": "output-1", "type": "output", "data": { "label": "Output Node", "sublabel": "Exit point", }, "position": {"x": 50, "y": 250}, }, # Toolbar Node - With action buttons { "id": "toolbar-1", "type": "toolbar", "data": { "label": "Toolbar Node", "sublabel": "Click to see toolbar", "toolbarPosition": "top", }, "position": {"x": 250, "y": 50}, }, # Resizable Node - Can be resized by dragging corners { "id": "resizable-1", "type": "resizable", "data": { "label": html.Div( [ html.Strong("Resizable"), html.P("Drag corners", style={"fontSize": "11px", "margin": 0}), ] ), }, "position": {"x": 250, "y": 150}, "style": {"width": 150, "height": 80}, }, # Circle Node - Small animated indicator { "id": "circle-1", "type": "circle", "data": {"label": "A"}, "position": {"x": 290, "y": 260}, }, # Group Node - Container for other nodes { "id": "group-1", "type": "group", "data": {"label": "Group Container"}, "position": {"x": 450, "y": 30}, "style": {"width": 200, "height": 200}, }, # Child nodes inside the group { "id": "child-1", "type": "default", "data": {"label": "Child A"}, "position": {"x": 30, "y": 50}, "parentId": "group-1", "extent": "parent", }, { "id": "child-2", "type": "default", "data": {"label": "Child B"}, "position": {"x": 30, "y": 120}, "parentId": "group-1", "extent": "parent", }, ] edges = [ {"id": "e1", "source": "input-1", "target": "default-1"}, {"id": "e2", "source": "default-1", "target": "output-1"}, {"id": "e3", "source": "toolbar-1", "target": "resizable-1"}, {"id": "e4", "source": "resizable-1", "target": "circle-1"}, {"id": "e5", "source": "child-1", "target": "child-2"}, ] component = dmc.Paper( [ dmc.Text( "All 6 node types with their unique characteristics.", c="dimmed", size="sm", mb="md", ), dmc.SimpleGrid( cols={"base": 2, "sm": 3, "lg": 6}, spacing="xs", mb="md", children=[ dmc.Badge("Input", color="green", variant="light", size="sm"), dmc.Badge("Default", color="blue", variant="light", size="sm"), dmc.Badge("Output", color="violet", variant="light", size="sm"), dmc.Badge("Toolbar", color="cyan", variant="light", size="sm"), dmc.Badge("Resizable", color="orange", variant="light", size="sm"), dmc.Badge("Group", color="gray", variant="light", size="sm"), ], ), dash_flows.DashFlows( id="dash-flows-node-types", nodes=nodes, edges=edges, style={"height": "400px"}, fitView=True, showControls=True, showMiniMap=True, ), dmc.Table( data={ "head": ["Type", "Handles", "Use Case"], "body": [ ["input", "Source only", "Entry points, data sources"], ["default", "Both", "Processing, transformation"], ["output", "Target only", "Exit points, results"], ["toolbar", "Both", "Nodes with actions"], ["resizable", "Configurable", "Variable-size content"], ["group", "None", "Container for related nodes"], ["circle", "Both", "Status indicators"], ], }, striped=True, mt="md", ), ], p="md", withBorder=True, radius="md", ) ``` --- ### 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 | .. exec::docs.dash_flows.edge_types :code: false ```python # 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 .. exec::docs.dash_flows.custom_icons :code: false ```python # 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}, {"id": "e3", "source": "process-node", "target": "output-node", "animated": True}, {"id": "e4", "source": "text-only", "target": "output-node", "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 ```python # 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 | .. exec::docs.dash_flows.node_interactions :code: false ```python # 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 bottom - `UP` - Bottom to top - `RIGHT` - Left to right - `LEFT` - Right to left ```python 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 ) ``` .. exec::docs.dash_flows.elk_layouts :code: false ```python # 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: ```python dmc.MantineProvider([ dash_flows.DashFlows( colorMode="dark", # or "light" or "system" # ... other props ) ]) ``` .. exec::docs.dash_flows.theming_dark_mode :code: false ```python # 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 ```python dash_flows.DashFlows( showBackground=True, backgroundVariant="dots", # "dots", "lines", or "cross" backgroundGap=16, # Pattern spacing ) ``` #### Control Positions ```python controlsPosition="bottom-left" # Default miniMapPosition="bottom-right" # Default ``` --- ### Viewport Control Programmatically control the viewport: ```python # 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 | --- ### Troubleshooting #### Nodes Not Appearing - Ensure each node has `id`, `type`, `data`, and `position` - Check that container has explicit height: `style={"height": "500px"}` - Verify node IDs are unique strings #### Edges Not Connecting - Verify `source` and `target` match 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=True` to avoid initial triggers - Ensure component ID matches callback Input/Output - Check browser console for JavaScript errors #### Layout Not Applying - Ensure `layoutOptions` is a valid JSON string via `json.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](https://reactflow.dev/) - Underlying library - [Iconify Icon Search](https://icon-sets.iconify.design/) - Browse icons - [ELK Layout Options](https://eclipse.dev/elk/reference/options.html) - Layout configuration - [Dash Mantine Components](https://www.dash-mantine-components.com/) - UI components --- *Source: /pip/dash_flows* *Generated with dash-improve-my-llms*