SimpleTreeView examples: itemId/label items, checkbox multi-select, per-item disabled flags, custom icons, and the iconContainer expansion trigger.

Tree Simple

SimpleTreeView examples: itemId/label items, checkbox multi-select, per-item disabled flags, custom icons, and the iconContainer expansion trigger.


Overview

SimpleTreeView examples: itemId/label items, checkbox multi-select, per-item disabled flags, custom icons, and the iconContainer expansion trigger.

SimpleTreeView

SimpleTreeView is the lightweight, JSX-driven tree in dash-mui-charts (Community license — no key needed). Unlike the data-driven TreeView (a RichTreeView wrapper with an MUI store), SimpleTreeView renders its items as TreeItem JSX children — a lighter alternative that suits navigation sidebars and small static trees. This documentation site dogfoods it: the site's own sidebar navigation is a SimpleTreeView (NAV_ITEMS in app.py).

Item shape — itemId, not id

items = [
    {"itemId": "1", "label": "Applications", "children": [
        {"itemId": "1.1", "label": "Calendar"},
        {"itemId": "1.2", "label": "Chrome"},
    ]},
]

Items also support per-item flags directly on the dict:

Basic usage

from dash_mui_charts import SimpleTreeView

SimpleTreeView(
    id="nav",
    items=items,
    defaultExpandedItems=["1"],
)

Key props

expandIcon="ChevronRight", collapseIcon="ExpandMore", endIcon="Description".

icon toggles the node (default "content" toggles on the whole row).

Outputs (use as callback Inputs)

@callback(Output("out", "children"), Input("nav", "selectedItems"))
def show_selection(sel):
    return json.dumps(sel) if sel else "Select an item..."

Related pages


The docs sidebar, dogfooded

Before the boilerplate migration this site's navigation WAS a SimpleTreeView. The tree below is generated from the site's real navigation map (components/navbar.py), and clicking a leaf genuinely navigates — the component driving its own documentation, as it always did.

# File: docs/tree_simple/sidebar_example.py

"""The dogfooding demo: this documentation's own sidebar as a SimpleTreeView.

Before the boilerplate migration, this site's navigation WAS a
SimpleTreeView — the component navigating its own docs. The shell now uses
the network-standard navbar, so the dogfooding story lives here instead:
the tree below is built from the SAME source the real sidebar reads, so it
can never drift from the actual nav, and selecting a leaf genuinely
navigates, exactly as the old sidebar did.

That source moved with sync item 16. The sidebar used to come from a
hand-written FAMILIES map in `components/navbar.py`; it now comes from each
page's own frontmatter (`category:` + `order:`) ordered by
`lib.constants.CATEGORY_ORDER`. This module reads the frontmatter directly
rather than the page registry, because a docs demo is executed WHILE the
registry is still being built — the file on disk is the one source that is
complete at any moment.
"""
import re
from pathlib import Path

from dash import Input, Output, clientside_callback, html

from dash_mui_charts import SimpleTreeView
from lib.constants import CATEGORY_ORDER

_DOCS = Path(__file__).resolve().parent.parent

_ICON_BY_FAMILY = {
    "SparklineChart": "Timeline",
    "PieChart": "PieChart",
    "BarChart": "BarChart",
    "Heatmap": "GridOn",
    "ScatterChart": "ScatterPlot",
    "LineChart": "ShowChart",
    "CandlestickChart": "CandlestickChart",
    "LiveTradingChart": "TrendingUp",
    "CompositeChart": "Layers",
    "TreeView": "AccountTree",
    "Date & Time Pickers": "Schedule",
    "Reference": "MenuBook",
}


def _frontmatter(md):
    out = {}
    for key in ("name", "endpoint", "category", "order"):
        m = re.search(rf"^{key}:\s*(.+?)\s*$", md, re.M)
        if m:
            out[key] = m.group(1)
    return out


def _families():
    """``[(category, [(endpoint, name), ...]), ...]`` in sidebar order."""
    by_cat = {}
    for f in sorted(_DOCS.glob("**/*.md")):
        fm = _frontmatter(f.read_text())
        if not fm.get("endpoint") or not fm.get("category"):
            continue
        try:
            order = int(fm.get("order", 1000))
        except ValueError:
            order = 1000
        by_cat.setdefault(fm["category"], []).append(
            (order, fm["name"], fm["endpoint"]))
    known = [c for c in CATEGORY_ORDER if c in by_cat]
    extra = sorted(c for c in by_cat if c not in CATEGORY_ORDER)
    return [(c, [(e, n) for _o, n, e in sorted(by_cat[c])])
            for c in known + extra]


items = [
    {"itemId": "/", "label": "Home", "icon": "Home"},
    {"itemId": "/changelog", "label": "Changelog", "icon": "History"},
] + [
    {
        "itemId": f"group-{family}",
        "label": family,
        "icon": _ICON_BY_FAMILY.get(family, "Folder"),
        "children": [
            {"itemId": path, "label": name, "icon": "PlayArrow"}
            for path, name in entries
        ],
    }
    for family, entries in _families()
]

component = html.Div(
    [
        html.P(
            "Click a leaf to navigate these docs — the tree is generated "
            "from the site's real navigation map.",
            style={"color": "var(--mantine-color-dimmed)",
                   "fontSize": "14px"},
        ),
        SimpleTreeView(
            id="tree-simple-sidebar-demo",
            items=items,
            defaultExpandedItems=["group-TreeView"],
            itemChildrenIndentation="8px",
            sx={
                "& .MuiTreeItem-label": {"fontSize": "14px",
                                         "lineHeight": "1.6"},
                "& .MuiTreeItem-content": {"padding": "4px 10px",
                                           "borderRadius": "6px",
                                           "minHeight": "34px"},
            },
        ),
    ],
    style={"maxWidth": "320px"},
)


# The old shell's navigation callback, verbatim in spirit: leaf selection
# drives the URL. Groups (non-path ids) are ignored.
clientside_callback(
    """
    (selected) => {
        if (selected && typeof selected === 'string' && selected.startsWith('/')) {
            if (window.location.pathname !== selected) {
                return selected;
            }
        }
        return window.dash_clientside.no_update;
    }
    """,
    Output("url", "href", allow_duplicate=True),
    Input("tree-simple-sidebar-demo", "selectedItems"),
    prevent_initial_call=True,
)

:defaultExpanded: false :withExpandedButton: true


Live examples

# File: docs/tree_simple/demo.py

"""Tree Simple demo — rendered on /tree-simple via `.. exec::`.

Ported verbatim from the pre-migration pages/tree_simple.py (same ids, same callbacks).
"""
import json
from dash import html, callback, Input, Output

from dash_mui_charts import SimpleTreeView

# SimpleTreeView uses itemId (not id) and label
SIMPLE_ITEMS = [
    {"itemId": "1", "label": "Applications", "children": [
        {"itemId": "1.1", "label": "Calendar"},
        {"itemId": "1.2", "label": "Chrome"},
        {"itemId": "1.3", "label": "Webstorm"},
    ]},
    {"itemId": "2", "label": "Documents", "children": [
        {"itemId": "2.1", "label": "OSS", "children": [
            {"itemId": "2.1.1", "label": "MUI", "children": [
                {"itemId": "2.1.1.1", "label": "index.js"},
            ]},
        ]},
        {"itemId": "2.2", "label": "Personal", "children": [
            {"itemId": "2.2.1", "label": "resume.pdf"},
        ]},
    ]},
    {"itemId": "3", "label": "Bookmarks", "children": [
        {"itemId": "3.1", "label": "GitHub"},
        {"itemId": "3.2", "label": "Stack Overflow"},
    ]},
]

# Per-item disabled/disableSelection example
DISABLED_ITEMS = [
    {"itemId": "d1", "label": "Available", "children": [
        {"itemId": "d1.1", "label": "Item A"},
        {"itemId": "d1.2", "label": "Item B (disabled)", "disabled": True},
        {"itemId": "d1.3", "label": "Item C"},
    ]},
    {"itemId": "d2", "label": "Mixed", "children": [
        {"itemId": "d2.1", "label": "Selectable"},
        {"itemId": "d2.2", "label": "Not selectable", "disableSelection": True},
        {"itemId": "d2.3", "label": "Disabled", "disabled": True},
    ]},
]

section_style = {'marginBottom': '40px'}
output_style = {
    'fontSize': '12px', 'margin': 0,
    'padding': '10px 14px', 'borderRadius': '6px',
    'backgroundColor': '#f5f5f5',
    'border': '1px solid #ddd',
    'whiteSpace': 'pre-wrap',
    'maxHeight': '200px',
    'overflow': 'auto',
}

component = html.Div([
    # --- 1. Basic ---
    html.Div([
        html.H3("1. Basic SimpleTreeView"),
        html.P("Nested items with itemId and label fields.", style={'color': '#666'}),
        SimpleTreeView(
            id="tree-simple-basic",
            items=SIMPLE_ITEMS,
            defaultExpandedItems=["1", "2"],
        ),
    ], style=section_style),

    # --- 2. Selection ---
    html.Div([
        html.H3("2. Selection with Click Tracking"),
        html.P("Single selection with click output.", style={'color': '#666'}),
        SimpleTreeView(
            id="tree-simple-select",
            items=SIMPLE_ITEMS,
            defaultExpandedItems=["1", "2", "3"],
        ),
        html.Pre(id="tree-simple-select-out", children="Select an item...", style=output_style),
    ], style=section_style),

    # --- 3. Multi-select with checkboxes ---
    html.Div([
        html.H3("3. Checkbox Multi-Select"),
        html.P("multiSelect + checkboxSelection on SimpleTreeView.", style={'color': '#666'}),
        SimpleTreeView(
            id="tree-simple-checkbox",
            items=SIMPLE_ITEMS,
            defaultExpandedItems=["1", "2", "3"],
            multiSelect=True,
            checkboxSelection=True,
        ),
        html.Pre(id="tree-simple-checkbox-out", children="Check items...", style=output_style),
    ], style=section_style),

    # --- 4. Per-item disabled/disableSelection ---
    html.Div([
        html.H3("4. Per-Item Disabled & disableSelection"),
        html.P(
            "SimpleTreeView supports per-item disabled and disableSelection flags directly on items.",
            style={'color': '#666'},
        ),
        SimpleTreeView(
            id="tree-simple-disabled",
            items=DISABLED_ITEMS,
            defaultExpandedItems=["d1", "d2"],
        ),
    ], style=section_style),

    # --- 5. Custom icons ---
    html.Div([
        html.H3("5. Custom Icons"),
        html.P('expandIcon="Add", collapseIcon="Remove", endIcon="Description"', style={'color': '#666'}),
        SimpleTreeView(
            id="tree-simple-icons",
            items=SIMPLE_ITEMS,
            defaultExpandedItems=["1"],
            expandIcon="Add",
            collapseIcon="Remove",
            endIcon="Description",
        ),
    ], style=section_style),

    # --- 6. Icon container expansion ---
    html.Div([
        html.H3("6. Icon Container Expansion Trigger"),
        html.P('expansionTrigger="iconContainer" — only icon click expands.', style={'color': '#666'}),
        SimpleTreeView(
            id="tree-simple-icon-trigger",
            items=SIMPLE_ITEMS,
            defaultExpandedItems=["2"],
            expansionTrigger="iconContainer",
            expandIcon="ChevronRight",
            collapseIcon="ExpandMore",
        ),
    ], style=section_style),
])


@callback(
    Output("tree-simple-select-out", "children"),
    Input("tree-simple-select", "selectedItems"),
    prevent_initial_call=True,
)
def show_simple_select(sel):
    return json.dumps(sel, indent=2) if sel else "Select an item..."


@callback(
    Output("tree-simple-checkbox-out", "children"),
    Input("tree-simple-checkbox", "selectedItems"),
    prevent_initial_call=True,
)
def show_simple_checkbox(sel):
    return json.dumps(sel, indent=2) if sel else "Check items..."

:defaultExpanded: false :withExpandedButton: true


Source: /tree-simple

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: