Flows

Interactive node-based flow diagrams with React Flow integration for Plotly Dash

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

FeatureDescription
6 Node TypesInput, Output, Default, Group, Toolbar, Resizable, Circle
7 Edge TypesBezier, Straight, Step, SmoothStep, Button, Data, AnimatedSVG
Custom IconsDashIconify integration with flexible layouts
Auto LayoutsELK.js algorithms (layered, force, radial, stress)
Rich CallbacksClick, double-click, hover, context menu, selection events
ThemingGlass, solid, minimal presets with 6 color schemes
Dark ModeFull support via Mantine integration

Installation

Visit GitHub Repo · PyPI

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:

# 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:

TypeDescriptionHandles
inputEntry point nodes (green accent)Source only
outputExit point nodes (purple accent)Target only
defaultStandard processing nodesBoth
groupContainer for child nodesNone (container)
toolbarNodes with floating action toolbarBoth
resizableUser-resizable nodesConfigurable
circleSmall animated circular indicatorsBoth

Edge Types

Connect nodes with various edge styles:

TypeDescription
default / bezierSmooth curved connections
straightDirect line connections
stepRight-angle with sharp corners
smoothstepRight-angle with rounded corners
buttonEdge with interactive delete button
dataEdge displaying data labels inline
animatedsvgFlowing 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

PropTypeDescription
iconDashIconifyIcon component to display
iconColorstrBackground color for icon container
showIconboolToggle icon visibility
layoutstr"stacked" (vertical) or "horizontal" (two-column)

Content Modes

# 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:

PropTypeDescription
labelstrPrimary text (or title alias)
sublabelstrSecondary text below label
bodystrDescription text
statusstrVisual state: "initial", "loading", "success", "error"
multilineboolEnable 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

PropTriggerData
clickedNodeSingle clickNode object
doubleClickedNodeDouble clickNode object
contextMenuNodeRight-clickNode + position
hoveredNodeMouse enter/leaveNode ID or None
selectedNodesSelection changeList of node IDs
selectedEdgesSelection changeList of edge IDs
lastConnectionNew connectionSource/target info
deletedNodesNode deletionList of deleted IDs
deletedEdgesEdge deletionList of deleted IDs
droppedNodeExternal dropDrop 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

AlgorithmBest For
layeredHierarchical/directed graphs
org.eclipse.elk.forceOrganic, force-directed
org.eclipse.elk.radialConcentric circles
org.eclipse.elk.stressBalanced edge lengths

Direction Options (Layered)

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

PresetDescription
glassGlass morphism with blur effects (default)
solidSolid backgrounds
minimalClean, 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

PropDefaultDescription
showControlsTrueZoom +/- and fit view buttons
showMiniMapTrueOverview minimap
showBackgroundTrueCanvas background pattern
showDevToolsFalseDebug 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

PropDefaultDescription
minZoom0.5Minimum zoom level
maxZoom2Maximum zoom level
fitViewFalseAuto-fit on initial render
snapToGridFalseSnap nodes to grid
snapGrid[15, 15]Grid size for snapping

Interaction Props

Control how users interact with the flow:

PropDefaultDescription
nodesDraggableTrueAllow dragging nodes
nodesConnectableTrueAllow creating connections
elementsSelectableTrueAllow selecting elements
panOnDragTruePan canvas by dragging
zoomOnScrollTrueZoom with scroll wheel
zoomOnPinchTrueZoom with pinch gesture
zoomOnDoubleClickTrueZoom on double-click
multiSelectionKeyCode"Shift"Key for multi-select
deleteKeyCode"Backspace"Key to delete selected

Props Reference

Core Props

PropTypeDefaultDescription
idstrRequiredComponent ID for callbacks
nodeslist[]Array of node objects
edgeslist[]Array of edge objects
styledict{}Container style (height required!)

Display Props

PropTypeDefaultDescription
fitViewboolFalseAuto-fit on mount
showControlsboolTrueShow viewport controls
showMiniMapboolTrueShow minimap
showBackgroundboolTrueShow background pattern
backgroundVariantstr"dots""dots", "lines", "cross"
controlsPositionstr"bottom-left"Control panel position
miniMapPositionstr"bottom-right"Minimap position

Theme Props

PropTypeDefaultDescription
themePresetstr"glass""glass", "solid", "minimal"
colorSchemestr"default"Color scheme name
colorModestr"light""light", "dark", "system"

Layout Props

PropTypeDefaultDescription
layoutOptionsstrNoneELK layout JSON string
defaultViewportdictNoneInitial viewport {x, y, zoom}
minZoomfloat0.5Minimum zoom level
maxZoomfloat2Maximum zoom level

Callback Output Props

PropTypeDescription
selectedNodeslistCurrently selected node IDs
selectedEdgeslistCurrently selected edge IDs
clickedNodedictLast clicked node
doubleClickedNodedictLast double-clicked node
contextMenuNodedictRight-clicked node with position
hoveredNodedictCurrently hovered node
lastConnectiondictLast created connection
deletedNodeslistRecently deleted node IDs
deletedEdgeslistRecently deleted edge IDs
viewportdictCurrent 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

PropertyTypeDefaultDescription
smartHandlesboolFalseAuto-route edges to closest node side
helperLinesboolFalseShow alignment guides when dragging
helperLineThresholdnumber5Snap distance in pixels
addNodeOnEdgeDropboolFalseCreate node by dropping edge on canvas
animateLayoutboolFalseSmooth transitions between layouts
animateLayoutDurationnumber300Animation duration (ms)
enableUndoRedoboolFalseEnable history tracking
undoRedoMaxHistorynumber50Max history snapshots
undoRedoActiondictNoneTrigger undo/redo: {action: 'undo'}
undoRedoStatedictOutput{canUndo, canRedo, undoCount, redoCount}
computeActiondictNoneTrigger computation: {action: 'compute'}
computeResultdictOutput{traversalOrder, nodeInputs, timestamp}
toggleCollapseNodestringNoneGroup node ID to toggle collapse
collapsedGroupslistOutputCurrently collapsed group IDs
viewportOverlayslistNoneFloating annotations [{x, y, content, style}]
connectionDragThresholdnumber1Min drag distance before connection
zIndexModestring"default""default" or "elevate"
autoPanOnNodeFocusboolFalsePan when Tab-focusing nodes
nodesFocusableboolFalseEnable Tab navigation
edgesFocusableboolFalseEnable Tab for edges
ariaLabelConfigdictNoneARIA labels for regions
deleteElementsActiondictNoneProgrammatic deletion
edgeDroppedNodedictOutputNode created from dropped edge
nodeConnectionsdictOutputReal-time connection map

Troubleshooting

Nodes Not Appearing

Edges Not Connecting

Callbacks Not Firing

Layout Not Applying

Multi-Select Not Working

Dark Mode Issues


Resources

Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs: