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. Configure a symbol's size, weight, and scale through ImageSource.Symbol. 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.

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
        }
    }
};

Button menus represent actions: choosing an entry executes its command without changing the button's title. Use Picker<TItem> when the choices represent selection state.