New components library - Native UI

Menu

Displays a menu to the user — such as a set of actions or functions — triggered by a button.

Installation

Copy the following code into your app directory.

uv

uv run buridan add component menu
from components.ui.menu import menu

Anatomy

Use the following composition to build a Menu component.

menu.root( menu.trigger(), menu.portal( menu.positioner( menu.popup( menu.item(), menu.separator(), menu.group( menu.group_label(), menu.item(), ), menu.checkbox_item( menu.checkbox_item_indicator(), ), menu.radio_group( menu.radio_item( menu.radio_item_indicator(), ), ), menu.submenu_root( menu.submenu_trigger(), menu.portal( menu.positioner( menu.popup(), ), ), ), ), ), ), )

Example

Basic

A basic dropdown menu with labels and separators.

from components.ui.button import button
from components.ui.menu import menu


def menu_basic():
    return menu.root(
        menu.trigger(render_=button("Open", variant="outline")),
        menu.portal(
            menu.positioner(
                menu.popup(
                    menu.group(
                        menu.group_label("My Account"),
                        menu.item("Profile"),
                        menu.item("Billing"),
                        menu.item("Settings"),
                    ),
                    menu.separator(),
                    menu.group(
                        menu.item("Team"),
                        menu.submenu_root(
                            menu.submenu_trigger("Invite users"),
                            menu.portal(
                                menu.positioner(
                                    menu.popup(
                                        menu.item("Email"),
                                        menu.item("Message"),
                                        menu.separator(),
                                        menu.item("More..."),
                                    ),
                                    side="right",
                                    align="start",
                                    align_offset=-3,
                                    side_offset=0,
                                ),
                            ),
                        ),
                        menu.item("New Team"),
                    ),
                    menu.separator(),
                    menu.group(
                        menu.item("GitHub"),
                        menu.item("Support"),
                        menu.item("API", disabled=True),
                    ),
                    menu.separator(),
                    menu.group(menu.item("Log out")),
                    class_name="w-40",
                ),
                align="start",
            ),
        ),
    )

Use menu.submenu_root to nest secondary actions.

from components.ui.button import button
from components.ui.menu import menu


def menu_submenu():
    return menu.root(
        menu.trigger(render_=button("Open", variant="outline")),
        menu.portal(
            menu.positioner(
                menu.popup(
                    menu.group(
                        menu.item("Team"),
                        menu.submenu_root(
                            menu.submenu_trigger("Invite users"),
                            menu.portal(
                                menu.positioner(
                                    menu.popup(
                                        menu.item("Email"),
                                        menu.item("Message"),
                                        menu.submenu_root(
                                            menu.submenu_trigger("More options"),
                                            menu.portal(
                                                menu.positioner(
                                                    menu.popup(
                                                        menu.item("Calendly"),
                                                        menu.item("Slack"),
                                                        menu.separator(),
                                                        menu.item("Webhook"),
                                                    ),
                                                    side="right",
                                                    align="start",
                                                ),
                                            ),
                                        ),
                                        menu.separator(),
                                        menu.item("Advanced..."),
                                    ),
                                    side="right",
                                    align="start",
                                ),
                            ),
                        ),
                        menu.item("New Team"),
                    ),
                ),
                align="start",
            ),
        ),
    )

Shortcuts

Add menu.shortcut to show keyboard hints.

import reflex as rx

from components.ui.button import button
from components.ui.menu import menu


def menu_shortcuts() -> rx.Component:
    return menu.root(
        menu.trigger(
            render_=button("Open", variant="outline"),
        ),
        menu.portal(
            menu.positioner(
                menu.popup(
                    menu.group(
                        menu.group_label("My Account"),
                        menu.item("Profile", menu.shortcut("⇧⌘P")),
                        menu.item("Billing", menu.shortcut("⌘B")),
                        menu.item("Settings", menu.shortcut("⌘S")),
                    ),
                    menu.separator(),
                    menu.item("Log out", menu.shortcut("⇧⌘Q")),
                ),
            ),
        ),
    )

Icons

Combine icons with labels for quick scanning.

import reflex as rx

from components.icons.hugeicon import hi
from components.ui.button import button
from components.ui.menu import menu


def menu_icons() -> rx.Component:
    return menu.root(
        menu.trigger(
            render_=button("Open", variant="outline"),
        ),
        menu.portal(
            menu.positioner(
                menu.popup(
                    menu.item(hi("UserIcon"), "Profile"),
                    menu.item(hi("CreditCardIcon"), "Billing"),
                    menu.item(hi("Setting07Icon"), "Settings"),
                    menu.separator(),
                    menu.item(
                        hi("LogoutSquare01Icon"), "Log out", variant="destructive"
                    ),
                ),
            ),
        ),
    )

Checkboxes

Use menu.checkbox_item for toggles.

from reflex.experimental import ClientStateVar

from components.ui.button import button
from components.ui.menu import menu

show_status_bar = ClientStateVar.create("show_status_bar", True)
show_activity_bar = ClientStateVar.create("show_activity_bar", False)
show_panel = ClientStateVar.create("show_panel", False)


def menu_checkboxes():
    return menu.root(
        menu.trigger(render_=button("Open", variant="outline")),
        menu.portal(
            menu.positioner(
                menu.popup(
                    menu.group(
                        menu.group_label("Appearance"),
                        menu.checkbox_item(
                            "Status Bar",
                            menu.checkbox_item_indicator(),
                            default_checked=show_status_bar.value,
                            on_checked_change=show_status_bar.set_value(
                                ~show_status_bar.value
                            ),
                        ),
                        menu.checkbox_item(
                            "Activity Bar",
                            disabled=True,
                        ),
                        menu.checkbox_item(
                            "Panel",
                            menu.checkbox_item_indicator(),
                            default_checked=show_panel.value,
                            on_checked_change=show_panel.set_value,
                        ),
                    ),
                    class_name="w-40",
                ),
            ),
        ),
    )

Checkboxes Icons

Add icons to checkbox items.

import reflex as rx
from reflex.experimental import ClientStateVar

from components.icons.hugeicon import hi
from components.ui.button import button
from components.ui.menu import menu

email_notif = ClientStateVar.create("email_notif", True)
sms_notif = ClientStateVar.create("sms_notif", False)
push_notif = ClientStateVar.create("push_notif", True)


def menu_checkboxes_icons() -> rx.Component:
    return menu.root(
        email_notif,
        sms_notif,
        push_notif,
        menu.trigger(
            render_=button("Notifications", variant="outline"),
        ),
        menu.portal(
            menu.positioner(
                menu.popup(
                    menu.group(
                        menu.group_label("Notification Preferences"),
                        menu.checkbox_item(
                            hi("Mail01Icon"),
                            "Email notifications",
                            menu.checkbox_item_indicator(),
                            default_checked=email_notif.value,
                            on_checked_change=email_notif.set_value,
                        ),
                        menu.checkbox_item(
                            hi("Message01Icon"),
                            "SMS notifications",
                            menu.checkbox_item_indicator(),
                            default_checked=sms_notif.value,
                            on_checked_change=sms_notif.set_value,
                        ),
                        menu.checkbox_item(
                            hi("Notification01Icon"),
                            "Push notifications",
                            menu.checkbox_item_indicator(),
                            default_checked=push_notif.value,
                            on_checked_change=push_notif.set_value,
                        ),
                    ),
                    class_name="w-48",
                ),
            ),
        ),
    )

Radio Group

Use menu.radio_group for exclusive choices.

import reflex as rx
from reflex.experimental import ClientStateVar

from components.ui.button import button
from components.ui.menu import menu

panel_position = ClientStateVar.create("panel_position", "bottom")


def menu_radio_group() -> rx.Component:
    return menu.root(
        panel_position,
        menu.trigger(
            render_=button("Open", variant="outline"),
        ),
        menu.portal(
            menu.positioner(
                menu.popup(
                    menu.group(
                        menu.group_label("Panel Position"),
                        menu.radio_group(
                            menu.radio_item(
                                "Top",
                                menu.radio_item_indicator(),
                                value="top",
                            ),
                            menu.radio_item(
                                "Bottom",
                                menu.radio_item_indicator(),
                                value="bottom",
                            ),
                            menu.radio_item(
                                "Right",
                                menu.radio_item_indicator(),
                                value="right",
                            ),
                            value=panel_position.value,
                            on_value_change=panel_position.set_value,
                        ),
                    ),
                    class_name="w-32",
                ),
            ),
        ),
    )

Avatar

An account switcher dropdown triggered by an avatar.

import reflex as rx

from components.icons.hugeicon import hi
from components.ui.avatar import avatar
from components.ui.button import button
from components.ui.menu import menu


def menu_avatar() -> rx.Component:
    return menu.root(
        menu.trigger(
            render_=button(
                avatar.root(
                    avatar.image(src="https://github.com/shadcn.png", alt="shadcn"),
                    avatar.fallback("LR"),
                ),
                variant="ghost",
                size="icon",
                class_name="rounded-full",
            ),
        ),
        menu.portal(
            menu.positioner(
                menu.popup(
                    menu.group(
                        menu.item(hi("UserIcon"), "Account"),
                        menu.item(hi("CreditCardIcon"), "Billing"),
                        menu.item(hi("Notification01Icon"), "Notifications"),
                    ),
                    menu.separator(),
                    menu.item(
                        hi("LogoutSquare01Icon"),
                        "Sign Out",
                    ),
                ),
                align="end",
            ),
        ),
    )