Grouped sidebar#

Authored pages chunked into labeled sections — the Settings window. The pages work exactly like a single-tier app; section labels and dividers add visual structure when the destinations fall into categories.

Grouped settings sidebar — light theme Grouped settings sidebar — dark theme

How it works#

add_header(text) adds a quiet, non-interactive section label; add_separator() adds a divider. The pages added after a header read as belonging to it — the label’s color and the extra top margin carry the grouping, so the items stay flush (no indentation).

shell.add_header("Account")
with shell.add_page("profile", text="Profile", icon="person"):
    ...
shell.add_header("Notifications")
with shell.add_page("email", text="Email", icon="envelope"):
    ...

Group consistently — put everything under headers, rather than sprinkling loose top-level items among sections. A pinned footer item is the conventional exception.

Example#

 1"""Grouped sidebar — pages chunked into labeled sections (a Settings window).
 2
 3``add_header`` adds a quiet section label and ``add_separator`` a divider; the
 4pages between read as a group. Settings screens are the canonical case — related
 5panes gathered under Account, Notifications, Advanced. Group *consistently*:
 6everything under a header, not loose items sprinkled among sections.
 7"""
 8import bootstack as bs
 9
10with bs.AppShell(title="Settings", size=(860, 580)) as shell:
11    shell.commandbar.add_label("Settings", font="heading-md")
12    shell.commandbar.add_spacer()
13    shell.commandbar.add_button(icon="circle-half", on_click=bs.toggle_theme)
14
15    shell.add_header("Account")
16    with shell.add_page("profile", text="Profile", icon="person"):
17        with bs.VStack(fill="both", expand=True, anchor_items="w", gap=8, padding=20):
18            bs.Label("Profile", font="heading-lg")
19            bs.Label("Your name, avatar, and bio.")
20    with shell.add_page("security", text="Security", icon="shield-lock"):
21        with bs.VStack(fill="both", expand=True, anchor_items="w", gap=8, padding=20):
22            bs.Label("Security", font="heading-lg")
23            bs.Label("Password and two-factor authentication.")
24
25    shell.add_header("Notifications")
26    with shell.add_page("email", text="Email", icon="envelope"):
27        with bs.VStack(fill="both", expand=True, anchor_items="w", gap=8, padding=20):
28            bs.Label("Email notifications", font="heading-lg")
29    with shell.add_page("push", text="Push", icon="bell"):
30        with bs.VStack(fill="both", expand=True, anchor_items="w", gap=8, padding=20):
31            bs.Label("Push notifications", font="heading-lg")
32
33    shell.add_header("Advanced")
34    with shell.add_page("developer", text="Developer", icon="terminal"):
35        with bs.VStack(fill="both", expand=True, anchor_items="w", gap=8, padding=20):
36            bs.Label("Developer options", font="heading-lg")
37
38    shell.navigate("profile")
39
40shell.run()

When to use#

Use a grouped sidebar when authored pages fall into a few clear categories. If you find yourself wanting a section that collapses to hide its items, that is a content concern rather than navigation — compose an Accordion inside a custom sidebar. If a “section” is really a long list of records, use Master–detail (list).