Button

Run commands, show loading state, and attach native menus to buttons.

Button covers the ordinary text button, icon button, destructive action, loading action, and menu button without requiring separate controls.

var save = new Button
{
    Text = "Save changes",
    Icon = ImageSource.Symbol("checkmark"),
    Kind = ButtonStyle.Filled,
    Size = ButtonSize.Large,
    Command = viewModel.SaveCommand,
    IsLoading = Bind(vm => vm.IsSaving),
    IsEnabled = Bind(vm => vm.CanEdit)
};

Icon accepts a local ImageSource: an SF Symbol, bundle asset, or encoded image data. Subtitle adds secondary text where the selected native button configuration supports it. Use IconPlacement to put the image before, after, above, or below the title; IconSpacing controls the gap. IconSize configures SF Symbols; bundle and data images use their intrinsic size.

Icon = ImageSource.Bundle("brand-mark");

Remote URLs are intentionally limited to the full Image control, which owns asynchronous loading, placeholders, and failure handling.

Kinds and sizes

Kind controls the native button configuration:

  • Plain, Gray, Tinted, and Filled use the matching system styles.
  • FilledCapsule uses a filled button with a capsule-shaped background.
  • Glass, ClearGlass, and ProminentGlass use the iOS 26 glass styles. On earlier releases, they fall back to plain or filled buttons.

Size can be Mini, Small, Medium, or Large. Medium is the default. Leave Padding unset to keep the system content insets; set it only when the button needs a deliberate custom hit area or visual rhythm.

var remove = new Button
{
    Text = "Delete project",
    Icon = ImageSource.Symbol("trash"),
    Kind = ButtonStyle.Gray,
    IsDestructive = true,
    Padding = new Thickness(18, 12)
};

IsDestructive applies the platform's destructive treatment. It does not add confirmation by itself; present a dialog before executing an irreversible action.

Commands and loading

Set Command and, when needed, CommandParameter. The button tracks CanExecute, updates its enabled state when CanExecuteChanged fires, and executes the command when tapped.

IsLoading replaces the icon with an activity indicator while preserving the button's title and size. Binding it to the operation state avoids a second spinner next to the button:

Whether a loading button should remain tappable is an application decision. Disable it separately when duplicate submissions are not safe.

Assign a Menu to present native actions from the button:

var sort = new Button
{
    Text = "Sort",
    Icon = ImageSource.Symbol("arrow.up.arrow.down"),
    Kind = ButtonStyle.Tinted,
    Menu =
    {
        new MenuAction
        {
            Text = "Title",
            Command = Command.From(() => SortBy(SortMode.Title))
        },
        new MenuAction
        {
            Text = "Recently added",
            Command = Command.From(() => SortBy(SortMode.Recent))
        },
        new MenuAction
        {
            Text = "Clear sort",
            Command = Command.From(ClearSort),
            IsDestructive = true
        }
    }
};

Set SelectsFromMenu when the button represents the selected menu value. Choosing an action then updates the button's displayed title. Leave it false for command menus whose actions should not become button state.

Use Picker<TItem> when those choices are typed application data rather than a list of button actions.