Skip to main content
Ctrl+K
bootstack is in pre-release — the API may still change before 1.0. Install with pip install --pre bootstack.
bootstack - Home bootstack - Home
  • User Guide
  • Widgets
  • API Reference
  • Production
  • GitHub
  • User Guide
  • Widgets
  • API Reference
  • Production
  • GitHub

Application

  • App
  • AppShell
  • Window

Actions

  • Button
  • ButtonGroup

Inputs

  • TextField
  • PasswordField
  • NumberField
  • Slider
  • RangeSlider
  • PathField
  • SpinnerField
  • TextArea
  • CodeEditor
  • DateField
  • TimeField

Selection

  • Checkbox
  • Select
  • Switch
  • ToggleButton
  • RadioGroup
  • ToggleGroup
  • SelectButton
  • Calendar

Data Display

  • Label
  • Badge
  • ProgressBar
  • Gauge
  • ListView
  • DataTable
  • Tree

Media

  • Picture
  • Gallery
  • Carousel
  • Avatar

Layout

  • Separator
  • Card
  • GroupBox
  • VStack
  • HStack
  • Grid
  • Accordion
  • ScrollView
  • SplitView

Chrome bars and menus

  • Menubar
  • CommandBar
  • StatusBar
  • MenuButton
  • ContextMenu

Navigation

  • PageStack
  • Tabs

Overlays

  • Tooltip
  • Toasts & Notifications

Dialogs

  • Message Dialogs
  • Input Dialogs
  • Color Dialog
  • Font Dialog
  • Filter Dialog
  • Dialog
  • Form Dialog

Forms

  • Form
  • Widgets
  • Menubar

Menubar#

A cross-platform application menu bar, built through app.menubar (also on window.menubar and shell.menubar). On Windows and Linux it renders as a themed strip at the top of the window; on macOS it relocates to the native global menu bar at the top of the screen. You write the same code for both.

Menubar with an open menu — light theme Menubar with an open menu — dark theme

The menu bar is single layer — a row of top-level menus, each holding a flat list of items. There are no sub-menus inside menus, which keeps the API small and the result consistent across platforms.

Usage#

Adding menus#

Open app.menubar.add_menu(...) as a context manager and add items to it. Each menu becomes a top-level entry (File, Edit, …).

with bs.App(title="Editor") as app:
    with app.menubar.add_menu("File") as file:
        file.add_action("New",  shortcut="Mod+N", on_click=new_file)
        file.add_action("Open", shortcut="Mod+O", on_click=open_file)
        file.add_separator()
        file.add_action("Quit", shortcut="Mod+Q", on_click=app.close)

    with app.menubar.add_menu("Edit") as edit:
        edit.add_action("Undo", shortcut="Mod+Z", on_click=undo)
        edit.add_action("Redo", shortcut="Mod+Shift+Z", on_click=redo)
app.run()

Item types#

A menu holds four kinds of item:

  • add_action(text, ...) — a command that fires on_click when chosen.

  • add_check(text, checked=...) — a toggle with an on/off state.

  • add_radio(text, value=..., group=...) — one choice within a named group.

  • add_separator() — a horizontal divider.

with app.menubar.add_menu("View") as view:
    view.add_check("Word wrap", checked=True, on_click=toggle_wrap)
    view.add_separator()
    view.add_radio("Light", value="light", group="theme",
                   on_click=lambda: bs.set_theme("bootstrap-light"))
    view.add_radio("Dark",  value="dark",  group="theme",
                   on_click=lambda: bs.set_theme("bootstrap-dark"))

Keyboard shortcuts#

shortcut= both displays the accelerator beside the item and binds the keypress so it actually fires:

  • A pattern like "Mod+S" is registered and bound automatically (Mod is Ctrl on Windows/Linux and ⌘ on macOS).

  • A registered key (one you set up yourself via the Shortcuts service) is shown as its accelerator but left bound where you registered it.

file.add_action("Save", shortcut="Mod+S", on_click=save)   # ⌘S / Ctrl+S, bound

Building from a dict#

app.menubar.load([...]) builds the whole bar declaratively — handy when the structure is data-driven. It produces the same result as the imperative form.

app.menubar.load([
    {"text": "File", "items": [
        {"text": "Open", "shortcut": "Mod+O", "on_click": open_file},
        {"type": "separator"},
        {"text": "Quit", "shortcut": "Mod+Q", "on_click": app.close},
    ]},
    {"text": "Edit", "items": [
        {"text": "Undo", "shortcut": "Mod+Z", "on_click": undo},
    ]},
])

macOS: the native menu bar#

On macOS the menus relocate to the native global menu bar at the top of the screen, rendered as a real NSMenu — text-only, with ⌘-style accelerators, exactly as macOS users expect. No code changes: the same app.menubar you build on Windows/Linux materializes natively on Mac.

Because the menu bar takes menus only, it translates to the native bar cleanly. Widgets (a theme toggle, a search box) go in the command bar instead — see below.

The command bar (App and Window)#

Widgets that aren’t menus — a search box, a theme toggle — belong in the command bar (app.commandbar), a CommandBar that shares the top row with the menu bar. On Windows/Linux you can fuse them into one row or stack them, and recolor the whole bar, via the menu_layout / chrome_surface / chrome_divider options on App and Window — see the App page for the command bar, layout, and surface options. (On AppShell, shell.menubar mounts above the built-in command bar; the fused/stacked layout is specific to App/Window.)

See also#

CommandBar — the command bar widget, usable standalone too.

MenuButton — a single button that opens an action menu.

ContextMenu — right-click / popup menu.

Keyboard shortcuts — the service behind shortcut=.

Full Example#

 1
 2with bs.App(title="Menu demo", size=(600, 380)) as app:
 3
 4    with app.menubar.add_menu("File") as file:
 5        file.add_action("New", icon="file-earmark-plus", shortcut="Mod+N",
 6                        on_click=lambda: bs.toast("New"))
 7        file.add_action("Open", icon="folder2-open", shortcut="Mod+O",
 8                        on_click=lambda: bs.toast("Open"))
 9        file.add_action("Save", icon="floppy", shortcut="Mod+S",
10                        on_click=lambda: bs.toast("Saved"))
11        file.add_separator()
12        file.add_action("Quit", shortcut="Mod+Q", on_click=app.close)
13
14    with app.menubar.add_menu("Edit") as edit:
15        edit.add_action("Undo", shortcut="Mod+Z", on_click=lambda: bs.toast("Undo"))
16        edit.add_action("Redo", shortcut="Mod+Shift+Z", on_click=lambda: bs.toast("Redo"))
17        edit.add_separator()
18        edit.add_check("Word wrap", checked=True,
19                       on_click=lambda: bs.toast("Toggled word wrap"))
20
21    with app.menubar.add_menu("View") as view:
22        view.add_radio("Zoom 50%",  value=0.5, group="zoom")
23        view.add_radio("Zoom 100%", value=1.0, group="zoom")
24        view.add_radio("Zoom 150%", value=1.5, group="zoom")
25
26    # Theme switcher: a toggle in the toolbar, fused to the right of the menus.
27    app.commandbar.add_spacer()
28    app.commandbar.add_button(icon="circle-half", on_click=bs.toggle_theme)
29
30    bs.Label("A sample menu, with a theme switcher in the toolbar.",
31             font="heading-md")
32
33app.run()

previous

SplitView

next

CommandBar

On this page
  • Usage
    • Adding menus
    • Item types
    • Keyboard shortcuts
    • Building from a dict
    • macOS: the native menu bar
    • The command bar (App and Window)
  • See also
  • Full Example

© Copyright 2026, Israel Dryer.

Created using Sphinx 9.1.0.

Built with the PyData Sphinx Theme 0.19.0.