Skip to content
IzzyBlazor 0.1.0-alpha.20

Enter all results ↑ ↓ move Esc close

SplitButton

Since 0.1.0-alpha.20
A main action with a menu of related ones beside it.

Static server-side rendering: no circuit, no WebAssembly.

Usage

razor
<SplitButton Label="Save" StartIcon="Lucide.Save" OnClick="Save" MenuLabel="More save options">
    <MenuItem OnClick="SaveAs">Save as…</MenuItem>
    <MenuItem Href="/drafts">Open drafts</MenuItem>
</SplitButton>

@* A link and a download, which work with no script *@
<SplitButton Label="Download PDF" Href="/report.pdf" download MenuLabel="Other formats">
    <MenuItem Href="/report.csv" download>CSV</MenuItem>
</SplitButton>

A SplitButton has a main action and, beside it, an arrow that opens a menu of related ones. Clicking the main button only runs its action, and the arrow only opens the menu. Use it when one action is the usual one; when none is, use a Menu.

The main action and its menu

Use Submit, Name and Value to post a form, as on a Button, and put MenuItems inside for the other choices. MenuLabel names the arrow for screen readers.

Preview

Open drafts
Nothing saved yet

Code

Actions.razor
@inject NavigationManager Navigation

<form method="post" @onsubmit="Posted" @formname="split-button-save" data-enhance>
    <AntiforgeryToken />
    <Row Gap="3" Wrap Align="Align.Center">
        <SplitButton Label="Save" StartIcon="Lucide.Save" Submit Name="intent" Value="save" OnClick='() => _pressed = "save"' MenuLabel="More save options" id="split-save">
            <MenuItem Submit Name="intent" Value="draft" OnClick='() => _pressed = "draft"'>Save as draft</MenuItem>
            <MenuItem Submit Name="intent" Value="close" OnClick='() => _pressed = "close"'>Save and close</MenuItem>
            <MenuDivider />
            <MenuItem Href="@Navigation.GetUriWithQueryParameter("save-open", "drafts")" Icon="Lucide.FileText">Open drafts</MenuItem>
        </SplitButton>
        <TextBlock As="span" Size="TextSize.Sm" Color="Color.Muted">@Result</TextBlock>
    </Row>
</form>

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

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

    // Interactive pages post nothing, so OnClick says which it was.
    private string? _pressed;
    private string? _posted;

    private string Result => _posted is not null ? $"Posted: {_posted}" : Opened is not null ? $"Opened: {Opened}" : "Nothing saved yet";

    private void Posted() => _posted = Intent ?? _pressed;
}

Static: The script adds the menu's keys, and Alt+Down on Save. Posts go without a full reload.

Choosing an item runs that item, and the main action stays as it was. To make the last choice the default, change Label and Href or OnClick yourself: the library doesn't remember one.

Use Href to make the main button a link, with External to open it in a new tab. Other attributes such as download go to the main button.

Only an icon

Use IconOnly with a StartIcon to leave out the text; Label stays as its name and tooltip.

Preview

Code

IconOnly.razor
<Row Gap="3" Wrap Align="Align.Center">
    <SplitButton Label="Paste" StartIcon="Lucide.ClipboardPaste" IconOnly MenuLabel="Paste options" Variant="ButtonVariant.Outline" Accent="Accent.Neutral">
        <MenuItem>Paste as text</MenuItem>
        <MenuItem>Paste as table</MenuItem>
    </SplitButton>
    <SplitButton Label="Export" StartIcon="Lucide.Download" IconOnly Href="favicon.svg" download="izzy.svg" MenuLabel="Other formats">
        <MenuItem Href="favicon.ico" download="izzy.ico">ICO</MenuItem>
    </SplitButton>
</Row>

Colors, weights and sizes

Use Accent, Variant and Size for both parts together, and FullWidth to fill the container with the main button. Unset, they come from a ButtonGroup or Toolbar around it.

Preview

Code

Looks.razor
<Column Gap="3" Align="Align.Start">
    <Row Gap="2" Wrap Align="Align.Center">
        <SplitButton Label="Solid" MenuLabel="Solid options"><MenuItem>Option</MenuItem></SplitButton>
        <SplitButton Label="Soft" Variant="ButtonVariant.Soft" MenuLabel="Soft options"><MenuItem>Option</MenuItem></SplitButton>
        <SplitButton Label="Outline" Variant="ButtonVariant.Outline" Accent="Accent.Neutral" MenuLabel="Outline options"><MenuItem>Option</MenuItem></SplitButton>
        <SplitButton Label="Ghost" Variant="ButtonVariant.Ghost" Accent="Accent.Neutral" MenuLabel="Ghost options"><MenuItem>Option</MenuItem></SplitButton>
        <SplitButton Label="Delete" Accent="Accent.Danger" MenuLabel="More ways to delete"><MenuItem Accent="Accent.Danger">Delete for everyone</MenuItem></SplitButton>
    </Row>
    <Row Gap="2" Wrap Align="Align.Center">
        <SplitButton Label="Small" Size="ControlSize.Sm" MenuLabel="Small options"><MenuItem>Option</MenuItem></SplitButton>
        <SplitButton Label="Medium" MenuLabel="Medium options"><MenuItem>Option</MenuItem></SplitButton>
        <SplitButton Label="Large" Size="ControlSize.Lg" MenuLabel="Large options"><MenuItem>Option</MenuItem></SplitButton>
    </Row>
</Column>

In a row of buttons

Put it in a ButtonGroup to join it to other buttons: only the row's own ends are round, and it takes the group's look.

Preview

Code

InGroup.razor
<Column Gap="3" Width="Length.Full">
    <ButtonGroup Label="Row" Variant="ButtonVariant.Outline" Accent="Accent.Neutral">
        <Button>Before</Button>
        <SplitButton Label="Middle" MenuLabel="Middle options"><MenuItem>Option</MenuItem></SplitButton>
        <Button>After</Button>
    </ButtonGroup>
    <Row Gap="3" Wrap Align="Align.Center">
        <ButtonGroup Label="Row starting with it" Variant="ButtonVariant.Outline" Accent="Accent.Neutral">
            <SplitButton Label="First" MenuLabel="First options"><MenuItem>Option</MenuItem></SplitButton>
            <Button>After</Button>
        </ButtonGroup>
        <ButtonGroup Label="Row ending with it" Variant="ButtonVariant.Outline" Accent="Accent.Neutral">
            <Button>Before</Button>
            <SplitButton Label="Last" MenuLabel="Last options"><MenuItem>Option</MenuItem></SplitButton>
        </ButtonGroup>
        <ButtonGroup Label="Row of one" Variant="ButtonVariant.Outline" Accent="Accent.Neutral">
            <SplitButton Label="Alone" MenuLabel="Alone options"><MenuItem>Option</MenuItem></SplitButton>
        </ButtonGroup>
    </Row>
    <ButtonGroup Label="Solid row">
        <Button>Before</Button>
        <SplitButton Label="Solid middle" MenuLabel="Solid middle options"><MenuItem>Option</MenuItem></SplitButton>
    </ButtonGroup>
    <SplitButton Label="Full width" FullWidth MenuLabel="Full width options" Variant="ButtonVariant.Soft"><MenuItem>Option</MenuItem></SplitButton>
</Column>

States

Use MainDisabled when there's no usual action but the others still work, and MenuDisabled when there's nothing else to choose. Disabled and Loading are for the whole control.

Preview

Code

States.razor
<Row Gap="3" Wrap Align="Align.Center">
    <SplitButton Label="Loading" Loading MenuLabel="Loading options"><MenuItem>Option</MenuItem></SplitButton>
    <SplitButton Label="Disabled" Disabled MenuLabel="Disabled options"><MenuItem>Option</MenuItem></SplitButton>
    <SplitButton Label="No default" MainDisabled MenuLabel="Choose a format" Variant="ButtonVariant.Outline" Accent="Accent.Neutral"><MenuItem>CSV</MenuItem></SplitButton>
    <SplitButton Label="No others" MenuDisabled MenuLabel="No other formats" Variant="ButtonVariant.Outline" Accent="Accent.Neutral"><MenuItem>CSV</MenuItem></SplitButton>
</Row>

Where the menu opens

Use Placement to open the menu elsewhere (by default it lines up with the start edge of the whole button) and PanelWidth for long items, as on a Menu.

Preview

Code

PanelWidth.razor
<SplitButton Label="Merge" MenuLabel="Other ways to merge" Placement="Placement.BottomEnd" PanelWidth="Length.Rem(18)" Variant="ButtonVariant.Outline" Accent="Accent.Neutral">
    <MenuItem>Squash and merge into one commit</MenuItem>
    <MenuItem>Rebase and merge, keeping each commit</MenuItem>
</SplitButton>

Running C# on click

Use OnClick on interactive pages, on the button and on each MenuItem.

Preview

Report

Nothing run yet

Code

OnClick.razor
<Column Gap="3" Align="Align.Start">
    <SplitButton Label="Run" StartIcon="Lucide.Play" OnClick='() => Did("Run")' Loading="_loading" Disabled="_disabled" MenuLabel="More ways to run" id="split-run">
        <MenuItem OnClick='() => Did("Run tests")'>Run tests</MenuItem>
        <MenuItem OnClick='() => Did("Run all")'>Run all</MenuItem>
    </SplitButton>
    @* A link whose state changes: it keeps what it has. *@
    <SplitButton Label="Report" Href="docs/components/menu" Loading="_loading" Disabled="_disabled" MainDisabled="_mainDisabled" OnClick='() => Did("Report")' MenuLabel="More reports" Variant="ButtonVariant.Outline" Accent="Accent.Neutral" id="split-report">
        <MenuItem OnClick='() => Did("Weekly")'>Weekly</MenuItem>
    </SplitButton>
    <Row Gap="4" Wrap>
        <Switch @bind-Value="_loading" Label="Loading" />
        <Switch @bind-Value="_disabled" Label="Disabled" />
        <Switch @bind-Value="_mainDisabled" Label="Report's main button disabled" />
    </Row>
    <TextBlock Size="TextSize.Sm" Color="Color.Muted">@_result</TextBlock>
</Column>

@code {
    private bool _loading;
    private bool _disabled;
    private bool _mainDisabled;
    private int _count;
    private string _result = "Nothing run yet";

    private void Did(string what) => _result = $"{what} ({++_count})";
}

Static: OnClick and the switches need an interactive page: nothing happens here. See Live.

In a Toolbar

Use ToolbarSplitButton, with a Toolbar's Position, Priority and Overflow. In the … menu it's a submenu named by MenuLabel, with the main action as its first item, then the same items. With CollapseLabels the main button drops its text and keeps its icon. See it on the Toolbar's page, and narrow the window to see them move.

razor
<Toolbar Label="Document">
    <ToolbarSplitButton Label="Save" StartIcon="Lucide.Save" Submit MenuLabel="More save options" Priority="1">
        <MenuItem Submit Name="intent" Value="draft">Save as draft</MenuItem>
    </ToolbarSplitButton>
</Toolbar>

Without script

The arrow opens and closes the menu natively, and links and form posts work, on the main button and on the items, as on a Menu. Alt+Down and the arrow keys need the script: Tab to the arrow and press Enter. OnClick runs only on interactive pages.

Parts

Written inside SplitButton. The API page lists their parameters.

Mode support

What works in each mode, SplitButton
FeatureStaticInteractive
Links, form posts and the menu opening and closingYesYes
Alt+Down on the main button; arrow keys, Home, End and type-ahead in the menuYesYes
OnClick on the main button and the itemsNoYes

Static: static server-side rendering, with no circuit and no WebAssembly. Interactive: Server, WebAssembly or Auto.

Without JavaScript

Every page starts as plain HTML: before IzzyBlazor's script loads on a static page, while an interactive page prerenders, and with JavaScript off. With JavaScript off, the menu opens and closes, but Alt+Down and the arrow keys don't work: Tab to the arrow and press Enter.

Accessibility

  • It renders a group named by Label, holding two native controls, each its own tab stop (in a Toolbar, its arrow keys move between them).
  • MenuLabel names the arrow and its menu, for screen readers and as its tooltip; it defaults to "Save: more options".
  • With IconOnly, Label becomes the main button's aria-label and tooltip.
  • While Loading, both parts keep focus and stay in the tab order, a link too, and neither does anything; the main button sets aria-busy, and both set aria-disabled.
  • Disabled, MainDisabled and MenuDisabled take the part out of the tab order, and a part that's focused as that happens loses focus, as a disabled button does.
  • If the arrow is blocked while its menu is open, the menu closes, so nothing in it runs twice.
  • With JavaScript off, the items are plain links and buttons with no menu roles, as in Menu, and only Tab, Enter, Space and Escape work.

Keyboard navigation

On the main button

SplitButton keys, On the main button
Press this keyTo do this
EnterRun its action
SpaceRun its action, when it isn't a link
Alt+DownOpen the menu at its first item
TabGo on to the arrow

On the arrow

SplitButton keys, On the arrow
Press this keyTo do this
Enter or Space or Down or Alt+DownOpen the menu at its first item
UpOpen the menu at its last item

In the menu

SplitButton keys, In the menu
Press this keyTo do this
Up or DownMove through the items, wrapping and skipping disabled ones
Home or EndGo to the first or last item
Enter or SpaceChoose the item
EscapeClose the menu and go back to the part that opened it
TabClose the menu and move on

Parameters

SplitButton parameters
PropTypeDefault
LabelThe main button's text, or with IconOnly its name for screen readers and its tooltip.stringRequired
StartIconAn icon before the main button's text.IconData?
IconOnlyShows only StartIcon on the main button, which it needs.boolfalse
HrefMakes the main button a link to this URL (an a).string?
ExternalFor a link: opens in a new tab with rel="noopener noreferrer" and shows an external-link icon.boolfalse
SubmitMakes the main button submit the form it's in (or Form), like type="submit".boolfalse
NamePosted with the form as Name=Value when the main button submits it, so one handler can tell it from the menu's items.string?
ValueThe value posted with Name.string?
FormActionSubmits to this URL instead of the form's own action (native formaction).string?
FormThe id of a form elsewhere on the page that the main button submits (native form).string?
MenuLabelThe name of the arrow that opens the menu, for screen readers and its tooltip, and of the submenu in a Toolbar's "…" menu.string?computed
AccentThe color of both parts.Accent?computed
VariantThe weight of both parts.ButtonVariant?computed
SizeThe height of both parts.ControlSize?computed
DisabledDisables both parts.boolfalse
LoadingShows a spinner on the main button and blocks both parts, which keep focus.boolfalse
LoadingLabelRead to screen readers while Loading.string?"Loading"
NewTabLabelRead to screen readers after an external link's text.string?"(opens in a new tab)"
FullWidthStretches to the full width of its container; the main button takes the extra width.boolfalse
MainDisabledDisables only the main button, such as when no default is available but the items still are.boolfalse
MenuDisabledDisables only the arrow, such as when there's nothing else to choose.boolfalse
PlacementWhere the menu opens, against the whole button.PlacementPlacement.BottomStart
PanelWidthThe width of the menu panel, as on Menu.Length?computed
ChildContentThe MenuItems, MenuGroups and MenuDividers.RenderFragment?
OnClickRuns when the main button is clicked on interactive pages (a link still navigates).EventCallback<MouseEventArgs>

See also

  • Button: submits, resets, links or runs C#.
  • ButtonGroup: joined or spaced buttons with shared defaults.
  • Menu: links, form posts and actions in a popover menu.
  • Toolbar: one row of controls; what doesn't fit moves into a … menu.

Connection lost

Rejoining the server…