R

ReflexKR

Reflex-Dokumentation

Die Komponentenwerkstatt: Reflex' 100 Bibliotheksbausteine

·

Kursüberblick

Layout

·

Box

·

Flex

·

Stack

·

Grid

·

Container

·

Section

·

Center

·

Spacer

·

Separator

·

Card

·

Inset

·

Aspect Ratio

·

Fragment

Typografie

·

Text

·

Heading

·

Link

·

Code

·

Markdown

·

Blockquote

·

Quote

·

Strong

·

Em

·

Kbd

Formulare

·

Button

·

Input

·

Text Area

·

Checkbox

·

Switch

·

Radio Group

·

Select

·

Slider

·

Form

·

Upload

Datenanzeige

·

Badge

·

Avatar

·

Icon

·

Callout

·

List

·

Data List

·

Code Block

·

Progress

·

Spinner

·

Scroll Area

·

Moment

Auf- und Zuklappen

·

Tabs

·

Accordion

·

Segmented Control

Overlays

·

Tooltip

·

Popover

·

Hover Card

·

Dialog

·

Alert Dialog

·

Drawer

·

Dropdown Menu

·

Context Menu

·

Toast

Dynamisches Rendern

·

Cond

·

Match

·

Foreach

·

Auto Scroll

Medien

·

Image

·

Audio

·

Video

Tabellen und Datengrids

·

Table

·

Data Table

·

Data Editor

Diagramme

·

Area Chart

·

Bar Chart

·

Line Chart

·

Composed Chart

·

Pie Chart

·

Radar Chart

·

Radial Bar Chart

·

Scatter Chart

·

Funnel Chart

·

Treemap

·

Sankey Chart

·

Error Bar

Diagrammbausteine

·

Axis

·

Cartesian Grid

·

Chart Tooltip

·

Legend

·

Label

·

Reference

·

Brush

Andere Diagrammbibliotheken

·

Plotly

·

Pyplot

HTML-Elemente

·

HTML

·

HTML Text

·

HTML Layout

·

HTML Forms

·

HTML Media

·

HTML Tables

·

SVG

Weitere Werkzeuge

·

Theme

·

Skeleton

·

Clipboard

·

Html Embed

·

Script

·

Memo

Die Komponentenwerkstatt: Reflex' 100 Bibliotheksbausteine
Overlays

Dropdown Menu

rx.menu.rootrx.menu.triggerrx.menu.contentrx.menu.itemrx.menu.separatorrx.menu.subrx.menu.sub_triggerrx.menu.sub_content

Offizielle Doku

Werkstattauftrag

Hänge in der Dateiliste ans Ende jeder Zeile einen „⋯“-Button. Ein Klick darauf soll ein Menü aufklappen, in dem man Duplizieren, Verschieben oder einen Statuswechsel wählt.

Ein Dropdown-Menü ist eine Liste von Optionen, die sich unter einem Button aufklappt, sobald man ihn anklickt. Man sammelt damit selten genutzte Aktionen hinter einem einzigen Button und hält die Oberfläche schlank. rx.menu und rx.dropdown_menu sind dasselbe; diese Seite verwendet das kürzere rx.menu.

  • menu.root: umschließt alle Teile des Menüs.
  • menu.trigger: umschließt das Element, das das Menü per Klick öffnet und schließt.
  • menu.content: das Panel, das sich im geöffneten Zustand aufklappt.
  • menu.item: ein einzelner Menüeintrag. Mit shortcut steht rechts daneben ein Tastenkürzel.
  • menu.separator: eine Trennlinie zwischen Einträgen.
  • menu.sub: umschließt alle Teile eines Untermenüs. Darin stehen menu.sub_trigger (der Eintrag, der das Untermenü öffnet) und menu.sub_content (das aufklappende Unterpanel).
rx.menu.root(
    rx.menu.trigger(
        rx.button("Options", variant="soft"),
    ),
    rx.menu.content(
        rx.menu.item("Edit", shortcut="⌘ E"),
        rx.menu.item("Duplicate", shortcut="⌘ D"),
        rx.menu.separator(),
        rx.menu.item("Archive", shortcut="⌘ N"),
        rx.menu.sub(
            rx.menu.sub_trigger("More"),
            rx.menu.sub_content(
                rx.menu.item("Move to project…"),
                rx.menu.item("Move to folder…"),
                rx.menu.separator(),
                rx.menu.item("Advanced options…"),
            ),
        ),
        rx.menu.separator(),
        rx.menu.item("Share"),
        rx.menu.item("Add to favorites"),
        rx.menu.separator(),
        rx.menu.item("Delete", shortcut="⌘ ⌫", color="red"),
    ),
)

Props ausprobieren

Aussehen und Position des Menüpanels gehören an menu.content. Ändere einen Wert und klicke dann auf Options.

Props ausprobieren

variant

size

color

high_contrast

side

align

disabled

modal

rx.menu.root(
    rx.menu.trigger(rx.button("Options", variant="soft")),
    rx.menu.content(
        rx.menu.item("Edit", shortcut="⌘ E"),
        rx.menu.item("Duplicate", shortcut="⌘ D", disabled=False),
        rx.menu.separator(),
        rx.menu.item("Delete", shortcut="⌘ ⌫", color_scheme="red"),
        variant="solid",
        size="2",
        color_scheme="indigo",
        high_contrast=False,
        side="bottom",
        align="start",
    ),
    modal=True,
)
variant="solid" färbt den Eintrag unter der Maus kräftig ein, "soft" dezent. Einträge mit disabled lassen sich nicht auswählen.
TeilPropWerteVerwendung
rootopen · default_openTrue / FalseOffen-Zustand über den State steuern (kontrolliert) · anfangs offen
rootmodalTrue / FalseStandard True. Sperrt, solange offen, Bedienung und Scrollen außerhalb
rooton_open_changeEventBeim Öffnen oder Schließen; übergibt den neuen Offen-Wert (bool)
contentvariant · size"solid" "soft" · "1" "2"Hervorhebungsstil, Größe
contentcolor_scheme · high_contrastFarbname · boolFarbe der Einträge, Kontrast
contentside · align · side_offset · align_offsetPositionSeite, zu der es aufklappt, und Ausrichtung
contentloopTrue / FalseMit der Tastatur vom Ende wieder zum Anfang springen
contentavoid_collisions · collision_padding · sticky · hide_when_detached-Verhalten am Bildschirmrand (wie beim Popover)
itemshortcutStringTastenkürzel, das rechts angezeigt wird (nur Anzeige)
itemcolor_scheme · disabled · text_valueFarbe · bool · StringFarbe des Eintrags, nicht auswählbar, Text für die Tipp-Suche
itemon_selectEventWenn ein Eintrag per Maus oder Tastatur gewählt wird
subopen · default_open · on_open_change-Offen-Zustand des Untermenüs
sub_triggerdisabled · text_value-Eintrag, der das Untermenü öffnet
sub_contentloop · side_offset · align_offset usw.-Position des Unterpanels

content und sub_content haben die Events on_escape_key_down, on_pointer_down_outside, on_focus_outside und on_interact_outside; content hat zusätzlich on_close_auto_focus.

Wenn ein Eintrag gewählt wird: on_select

Klickt man einen Eintrag an oder wählt ihn per Tastatur, wird on_select aufgerufen und das Menü schließt sich. Übergibst du den gewählten Wert als Argument an den Handler, brauchst du nicht für jeden Eintrag einen eigenen Handler. Auch on_click wie im Dialog-Beispiel der offiziellen Doku wird bei Maus und Tastatur ausgelöst, das eigens für die Auswahl gedachte Event ist aber on_select.

Selected: nothing yet

class DropdownPickState(rx.State):
    last: str = "nothing yet"

    @rx.event
    def pick(self, name: str):
        self.last = name


rx.flex(
    rx.menu.root(
        rx.menu.trigger(rx.button("Sort by", rx.icon("chevron-down"), variant="outline")),
        rx.menu.content(
            rx.menu.item("Name", on_select=DropdownPickState.pick("Name")),
            rx.menu.item("Date added", on_select=DropdownPickState.pick("Date added")),
            rx.menu.item("Size", on_select=DropdownPickState.pick("Size")),
            rx.menu.separator(),
            rx.menu.item("Custom…", disabled=True),
        ),
    ),
    rx.text("Selected: ", rx.text.strong(DropdownPickState.last)),
    spacing="3",
    align="center",
    wrap="wrap",
)

Beim Öffnen und Schließen: on_open_change

on_open_change an menu.root übergibt beim Öffnen True und beim Schließen False.

Opened or closed: 0 times

Dropdown menu open: no

class DropdownMenuEventState(rx.State):
    num_opens: int = 0
    opened: bool = False

    @rx.event
    def count_opens(self, value: bool):
        self.opened = value
        self.num_opens += 1


rx.flex(
    rx.text("Opened or closed: ", DropdownMenuEventState.num_opens, " times"),
    rx.text("Dropdown menu open: ", rx.cond(DropdownMenuEventState.opened, "yes", "no")),
    rx.menu.root(
        rx.menu.trigger(
            rx.button("Options", variant="soft", size="2"),
        ),
        rx.menu.content(
            rx.menu.item("Edit", shortcut="⌘ E"),
            rx.menu.item("Duplicate", shortcut="⌘ D"),
            rx.menu.separator(),
            rx.menu.item("Archive", shortcut="⌘ N"),
            rx.menu.separator(),
            rx.menu.item("Delete", shortcut="⌘ ⌫", color="red"),
        ),
        on_open_change=DropdownMenuEventState.count_opens,
    ),
    direction="column",
    spacing="3",
    align="start",
)

Einen Dialog aus dem Menü öffnen

Steckst du ein komplettes rx.dialog.root in einen Menüeintrag, verschwindet der Dialog in dem Moment, in dem sich das Menü schließt. Er liegt ja im Menü und existiert deshalb nur, solange das Menü offen ist.

rx.menu.root(
    rx.menu.trigger(rx.icon("ellipsis-vertical")),
    rx.menu.content(
        rx.menu.item(
            rx.dialog.root(  # So bitte nicht
                rx.dialog.trigger(rx.text("Edit")),
                rx.dialog.content(...),
            ),
        ),
    ),
)

Lege den Dialog stattdessen außerhalb des Menüs ab und merke dir in einem einzigen State-String, welches Fenster offen ist. Der Eintrag ändert nur diesen Wert, und jeder Dialog öffnet sich über open=State.wert == "name". Beim Schließen setzt on_open_change den Wert auf einen leeren String zurück.

class DropdownDialogState(rx.State):
    which_dialog_open: str = ""

    @rx.event
    def set_which_dialog_open(self, value: str):
        self.which_dialog_open = value

    @rx.event
    def delete(self):
        yield rx.toast("Deleted item")

    @rx.event
    def save_settings(self):
        yield rx.toast("Saved settings")


rx.vstack(
    rx.menu.root(
        rx.menu.trigger(rx.icon_button(rx.icon("menu"), variant="soft", aria_label="Menu")),
        rx.menu.content(
            rx.menu.item("Delete", on_click=DropdownDialogState.set_which_dialog_open("delete")),
            rx.menu.item("Settings", on_click=DropdownDialogState.set_which_dialog_open("settings")),
        ),
    ),
    rx.cond(
        DropdownDialogState.which_dialog_open,
        rx.text(DropdownDialogState.which_dialog_open, " dialog is open"),
    ),
    rx.alert_dialog.root(
        rx.alert_dialog.content(
            rx.alert_dialog.title("Are you Sure?"),
            rx.alert_dialog.description(
                "This action cannot be undone. Are you sure you want to delete this item?",
                margin_bottom="20px",
            ),
            rx.hstack(
                rx.alert_dialog.action(
                    rx.button("Delete", color_scheme="red", on_click=DropdownDialogState.delete),
                ),
                rx.spacer(),
                rx.alert_dialog.cancel(rx.button("Cancel")),
            ),
        ),
        open=DropdownDialogState.which_dialog_open == "delete",
        on_open_change=DropdownDialogState.set_which_dialog_open(""),
    ),
    rx.dialog.root(
        rx.dialog.content(
            rx.dialog.title("Settings"),
            rx.dialog.description("Set your settings in this settings dialog.", margin_bottom="20px"),
            rx.dialog.close(
                rx.button("Close", on_click=DropdownDialogState.save_settings),
            ),
        ),
        open=DropdownDialogState.which_dialog_open == "settings",
        on_open_change=DropdownDialogState.set_which_dialog_open(""),
    ),
    align="center",
)

Auftrag erledigt

Der „⋯“-Button am Zeilenende bekommt ein Menü. Verschieben und Statuswechsel sind in Untermenüs gebündelt, und der gewählte Wert landet über on_select im State, der den Text in der Zeile ändert. Da der Button ganz rechts in der Zeile sitzt, richten wir das Panel mit align="end" aus.

Q3 report

Inbox · Draft

class DropdownRowState(rx.State):
    status: str = "Draft"
    folder: str = "Inbox"

    @rx.event
    def set_status(self, value: str):
        self.status = value

    @rx.event
    def move(self, folder: str):
        self.folder = folder

    @rx.event
    def duplicate(self):
        return rx.toast("Duplicated Q3 report")


rx.flex(
    rx.icon("file-text", color=rx.color("gray", 10)),
    rx.box(
        rx.text("Q3 report", weight="bold"),
        rx.text(DropdownRowState.folder, " · ", DropdownRowState.status, size="1", color_scheme="gray"),
    ),
    rx.spacer(),
    rx.menu.root(
        rx.menu.trigger(rx.icon_button(rx.icon("ellipsis"), variant="ghost", color_scheme="gray", aria_label="More actions")),
        rx.menu.content(
            rx.menu.item("Duplicate", shortcut="⌘ D", on_select=DropdownRowState.duplicate),
            rx.menu.sub(
                rx.menu.sub_trigger("Move to"),
                rx.menu.sub_content(
                    rx.menu.item("Inbox", on_select=DropdownRowState.move("Inbox")),
                    rx.menu.item("Projects", on_select=DropdownRowState.move("Projects")),
                    rx.menu.item("Archive", on_select=DropdownRowState.move("Archive")),
                ),
            ),
            rx.menu.sub(
                rx.menu.sub_trigger("Status"),
                rx.menu.sub_content(
                    rx.menu.item("Draft", on_select=DropdownRowState.set_status("Draft")),
                    rx.menu.item("Published", on_select=DropdownRowState.set_status("Published")),
                ),
            ),
            rx.menu.separator(),
            rx.menu.item("Delete", shortcut="⌘ ⌫", color_scheme="red", disabled=True),
            align="end",
        ),
    ),
    spacing="3",
    align="center",
    padding="8px 12px",
    border=f"1px solid {rx.color('gray', 5)}",
    border_radius="8px",
    max_width="420px",
)

Werkstattnotizen

  • rx.menu = rx.dropdown_menu. root > trigger + content > item · separator · sub(sub_trigger, sub_content).

  • Wird ein Eintrag gewählt, wird on_select aufgerufen und das Menü schließt sich. Werte als Handler-Argument übergeben.

  • shortcut zeigt nur den Text an. Das eigentliche Tastenkürzel muss man selbst bauen.

  • Aussehen und Position an content (variant, size, color_scheme, side, align).

  • Einen Dialog aus dem Menü öffnen: Dialog außerhalb des Menüs ablegen und über den State öffnen.

← Zurück

Overlays · Drawer

Weiter →

Overlays · Context Menu

Über neue Lektionen informiert werden

Wir schicken dir eine E-Mail, wenn dieser Kurs neue Lektionen bekommt oder ein Folgekurs erscheint.

Erhoben: deine E-Mail-Adresse · Zweck: Hinweise auf neue Lektionen oder den Start dieses Kurses · Speicherdauer: bis du die Löschung verlangst. Keine andere Nutzung, keine Weitergabe. Antworte einfach auf eine Benachrichtigung, dann löschen wir sie sofort.

Fragen & Antworten

Frag alles zu dieser Lektion. Fragen aus allen Sprachversionen dieser Seite erscheinen hier gemeinsam.

Wird als reiner Text angezeigt, ohne Links oder Formatierung.

R

ReflexKR

Python und Reflex zum Anfassen. Alle Beispiele wurden mit Reflex 0.10.0 ausgeführt und geprüft.

ReflexKR ist kostenlos und ohne Werbebanner. Wenn es dir geholfen hat, kannst du es mit einem Kaffee unterstützen. ☕ Spendier uns einen Kaffee

Hinweis zu Affiliate-Links
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex