Skip to content
IzzyBlazor 0.1.0-alpha.17

Enter all results ↑ ↓ move Esc close

Menu

Links, form posts and actions in a popover menu.

A static page with IzzyBlazor's script. No circuit.

Usage

razor
<Menu Label="Project" StartIcon="Lucide.Folder">
    @* A link, on any page. *@
    <MenuItem Href="settings" Icon="Lucide.Settings">Settings</MenuItem>

    @* Posts the form the menu is in, on any page. *@
    <MenuItem Submit Name="intent" Value="archive" Icon="Lucide.Archive">Archive</MenuItem>

    @* C# on click, on interactive pages. *@
    <MenuItem OnClick="Duplicate" Icon="Lucide.Copy">Duplicate</MenuItem>
</Menu>

Give each MenuItem an Href to go somewhere, Submit to post the form the menu is in, or OnClick to run C# on interactive pages. Links and posts work on static pages, with JavaScript off too.

Preview

Settings
Opened: settings

Code

Actions.razor
@inject NavigationManager Navigation

<form method="post" @onsubmit="Posted" @formname="project" data-enhance>
    <AntiforgeryToken />
    <Row Gap="3" Wrap Align="Align.Center">
        <Menu Label="Project" StartIcon="Lucide.Folder">
            <MenuItem Href="@Navigation.GetUriWithQueryParameter("open", "settings")" Icon="Lucide.Settings">Settings</MenuItem>
            <MenuItem Submit Name="intent" Value="duplicate" OnClick='() => _pressed = "duplicate"' Icon="Lucide.Copy">Duplicate</MenuItem>
            <MenuItem Icon="Lucide.Share2" Disabled>Share</MenuItem>
            <MenuDivider />
            <MenuItem Submit Name="intent" Value="delete" OnClick='() => _pressed = "delete"' Icon="Lucide.Trash" Accent="Accent.Danger">Delete</MenuItem>
        </Menu>
        <TextBlock As="span" Size="TextSize.Sm" Color="Color.Muted">@_result</TextBlock>
    </Row>
</form>

@code {
    // Settings is a link: ?open=settings.
    [SupplyParameterFromQuery(Name = "open")]
    private string? Opened { get; set; }

    // Static pages: the chosen item's Name=Value posts with the form.
    [SupplyParameterFromForm(FormName = "project", Name = "intent")]
    private string? Intent { get; set; }

    // Interactive pages post nothing, so OnClick says which item it was.
    private string? _pressed;
    private string _result = "Nothing chosen yet";
    private string? _opened;

    protected override void OnParametersSet()
    {
        if (Opened != _opened && Opened is not null)
        {
            _result = $"Opened: {Opened}";
        }

        _opened = Opened;
    }

    private void Posted() => _result = $"Posted: {Intent ?? _pressed}";
}

Static: The script adds the menu roles and keys: arrows, Home, End and type-ahead. Posts go without a full reload.

A submit item posts its Name and Value, so one form tells its items apart. FormAction posts to another address, and Form names a form the menu isn't in. Disabled shows an item that can't be chosen.

Groups, shortcuts and colors

Use MenuGroup for a labeled section and MenuDivider for a line between items. Icon adds an icon, Shortcut shows a key, and Accent="Accent.Danger" marks an item that destroys something.

Preview

Clipboard

Code

Groups.razor
<Menu Label="Edit" StartIcon="Lucide.Pencil">
    <MenuGroup Label="Clipboard">
        <MenuItem Icon="Lucide.Scissors" Shortcut="⌘+X">Cut</MenuItem>
        <MenuItem Icon="Lucide.Copy" Shortcut="⌘+C">Copy</MenuItem>
        <MenuItem Icon="Lucide.ClipboardPaste" Shortcut="⌘+V">Paste</MenuItem>
    </MenuGroup>
    <MenuDivider />
    <MenuItem Icon="Lucide.Trash" Shortcut="Del" Accent="Accent.Danger">Delete</MenuItem>
</Menu>

A shortcut is only shown: the app handles the keys.

Your own trigger

Use Trigger for a button of your own, such as an IconButton, and give it the menu's attributes with @attributes="context". Placement sets where the menu opens.

Preview

Website

Deployed 2 hours ago

12 pages, 340 visits this week.

Code

Trigger.razor
<Card Title="Website" Description="Deployed 2 hours ago" Width="20.Rem">
    <CardActions>
        <Menu Placement="Placement.BottomEnd">
            <Trigger>
                <IconButton Icon="Lucide.Ellipsis" Label="Website actions" Variant="ButtonVariant.Ghost" Accent="Accent.Neutral" @attributes="context" />
            </Trigger>
            <ChildContent>
                <MenuItem Icon="Lucide.Pencil">Rename</MenuItem>
                <MenuItem Icon="Lucide.Download">Export</MenuItem>
                <MenuItem Icon="Lucide.Archive">Archive</MenuItem>
            </ChildContent>
        </Menu>
    </CardActions>
    <TextBlock Size="TextSize.Sm" Color="Color.Muted">12 pages, 340 visits this week.</TextBlock>
</Card>

The panel fits its items, from 10rem to 24rem wide; PanelWidth sets a width of your own. Without a Trigger, Label, StartIcon, Accent, Variant and Size style the default button.

Checked items

Use a MenuRadioGroup with a QueryKey for options kept in the address, such as a sort, and Checked with an Href for an on/off item. Both are links, so they work on static pages.

Preview

Sorted by name, compact rows

Code

Checked.razor
@inject NavigationManager Navigation

<Row Gap="3" Wrap Align="Align.Center">
    <Menu Label="View" StartIcon="Lucide.SlidersHorizontal">
        @* Each option is a link that sets ?sort=; the URL says which is checked. *@
        <MenuRadioGroup Label="Sort by" QueryKey="sort" Value="@Sort">
            <MenuItem Value="name">Name</MenuItem>
            <MenuItem Value="date">Date modified</MenuItem>
            <MenuItem Value="size">Size</MenuItem>
        </MenuRadioGroup>
        <MenuDivider />
        @* A link that turns ?compact= on or off. *@
        <MenuItem Checked="Compact" Href="@Navigation.GetUriWithQueryParameter("compact", Compact ? null : "true")">Compact rows</MenuItem>
    </Menu>
    <TextBlock As="span" Size="TextSize.Sm" Color="Color.Muted">Sorted by @Sort@(Compact ? ", compact rows" : "")</TextBlock>
</Row>

@code {
    [SupplyParameterFromQuery(Name = "sort")]
    private string? SortQuery { get; set; }

    [SupplyParameterFromQuery(Name = "compact")]
    private bool Compact { get; set; }

    private string Sort => SortQuery ?? "name";
}

On an interactive page, @bind-Value on the group and @bind-Checked on an item keep them in C# instead. Name on the group makes its options submit buttons that post with a form, and KeepOpen on an item leaves the menu open, to tick several in a row.

Use MenuSub for an item that opens another menu beside it. Choosing an item in it closes the whole menu.

Preview

Code

Sub.razor
<Menu Label="Actions">
    <MenuItem Icon="Lucide.Pencil">Rename</MenuItem>
    <MenuSub Label="Move to" Icon="Lucide.FolderInput">
        <MenuItem Icon="Lucide.Inbox">Inbox</MenuItem>
        <MenuItem Icon="Lucide.Archive">Archive</MenuItem>
        <MenuSub Label="Projects" Icon="Lucide.Folder">
            <MenuItem>Website</MenuItem>
            <MenuItem>Course app</MenuItem>
        </MenuSub>
    </MenuSub>
    <MenuSub Label="Share" Icon="Lucide.Share2">
        <MenuItem Icon="Lucide.Link">Copy link</MenuItem>
        <MenuItem Icon="Lucide.Mail">Email</MenuItem>
    </MenuSub>
    <MenuDivider />
    <MenuItem Icon="Lucide.Trash" Accent="Accent.Danger">Delete</MenuItem>
</Menu>

Static: Hover and the right arrow open a submenu and the left arrow closes it, as on interactive pages.

Parts

Written inside Menu. The API page lists their parameters.

  • MenuItem: one entry in a Menu.
  • MenuGroup: a labeled section of a Menu.
  • MenuDivider: a line between groups of items in a Menu.
  • MenuRadioGroup: a set of menu options where one is chosen (menuitemradio), each a MenuItem with a Value.
  • MenuSub: a submenu: an item that opens a second menu beside it.

Mode support

What works in each mode, Menu
FeatureOffStaticLive
EverythingPartlyYesYes

Off: JavaScript disabled. Static: a static SSR page with IzzyBlazor's script. Live: an interactive page (Server, WebAssembly or Auto). With JavaScript off, the menu opens and closes, but arrow keys and type-ahead don't move between items.

Accessibility

  • With script, it's a menu named by its trigger, with menuitem, menuitemcheckbox and menuitemradio items (aria-checked); a MenuGroup is a group named by its Label.
  • Opening it focuses the first item; Down and Up on the trigger open it at the first or last. Escape closes it, and focus goes back to the trigger.
  • Down and Up move through the items and wrap, Home and End go to the ends, typing jumps to the next item starting with those letters, and Tab closes the menu. Disabled items are skipped.
  • Right opens a MenuSub and focuses its first item; Left closes it (the other way round on right-to-left pages).
  • With JavaScript off, items are plain links and buttons with no menu roles; a checked one has aria-pressed (buttons) or aria-current (links).
  • A custom Trigger needs a name of its own, such as an IconButton's Label. A Shortcut is display only: the app handles the keys.

Parameters

Menu parameters
PropTypeDefault
LabelThe default trigger's text.string?
AccentThe default trigger's color.AccentAccent.Neutral
VariantThe default trigger's weight.ButtonVariantButtonVariant.Outline
SizeThe default trigger's size.ControlSizeControlSize.Md
StartIconAn icon before the default trigger's text.IconData?
TriggerA custom trigger in place of the default button.RenderFragment<IReadOnlyDictionary<string, object>>?
PlacementWhere the menu opens.PlacementPlacement.BottomStart
PanelWidthThe width of the menu panel, such as Length.Rem(16) for a column of long labels.Length?computed
ChildContentThe MenuItems, MenuGroups and MenuDividers.RenderFragment?
@bind-OpenWhether it is open.boolfalse
IdThe element id.string?

See also