Skip to content
IzzyBlazor 0.1.0-alpha.17

Enter all results ↑ ↓ move Esc close

IconButton

A square button with a required label.

JavaScript off: what the HTML and CSS do on their own.

Usage

razor
<IconButton Icon="Lucide.Pencil" Label="Edit" Variant="ButtonVariant.Ghost" Accent="Accent.Neutral" />

@* A link. *@
<IconButton Icon="Lucide.ExternalLink" Label="Open the docs" Href="https://example.com/docs" />

@* In a form: posts delete=42 when pressed. *@
<IconButton Icon="Lucide.Trash" Label="Delete invoice 42" Submit Name="delete" Value="42"
            Accent="Accent.Danger" Variant="ButtonVariant.Ghost" />

An IconButton is a Button with one icon and no visible text, for actions people know by their icon: edit, delete, more. Label is required: it's the button's name and its tooltip. When the icon alone isn't clear, use a Button with text.

Labels and variants

Use Label for what the button does, and Variant, Accent and Href as on Button.

Preview

Code

Variants.razor
<Row Gap="2" Align="Align.Center" Wrap Justify="Justify.Center">
    <IconButton Icon="Lucide.Plus" Label="New project" />
    <IconButton Icon="Lucide.Settings" Label="Settings" Variant="ButtonVariant.Soft" />
    <IconButton Icon="Lucide.Pencil" Label="Edit" Variant="ButtonVariant.Outline" Accent="Accent.Neutral" />
    <IconButton Icon="Lucide.Ellipsis" Label="More actions" Variant="ButtonVariant.Ghost" Accent="Accent.Neutral" />
    <IconButton Icon="Lucide.Trash" Label="Delete" Variant="ButtonVariant.Ghost" Accent="Accent.Danger" />
    <IconButton Icon="Lucide.ExternalLink" Label="Open Lucide's icon list" Href="https://lucide.dev/icons" Variant="ButtonVariant.Outline" Accent="Accent.Neutral" />
</Row>

Ghost with a Neutral accent suits rows of tools, where a filled button for each would be loud; keep a solid one for the action that matters most.

Sizes and states

Use Size, Loading and Disabled as on Button.

Preview

Code

SizesStates.razor
<Row Gap="2" Align="Align.Center" Wrap Justify="Justify.Center">
    <IconButton Icon="Lucide.Bell" Label="Notifications" Size="ControlSize.Sm" Variant="ButtonVariant.Outline" Accent="Accent.Neutral" />
    <IconButton Icon="Lucide.Bell" Label="Notifications" Variant="ButtonVariant.Outline" Accent="Accent.Neutral" />
    <IconButton Icon="Lucide.Bell" Label="Notifications" Size="ControlSize.Lg" Variant="ButtonVariant.Outline" Accent="Accent.Neutral" />
    <IconButton Icon="Lucide.RefreshCw" Label="Refresh" Loading Variant="ButtonVariant.Outline" Accent="Accent.Neutral" />
    <IconButton Icon="Lucide.Share2" Label="Share" Disabled Variant="ButtonVariant.Outline" Accent="Accent.Neutral" />
</Row>

Actions on a row

Use Href for a link and Submit with Name and Value to post which row a button is on, with OnClick for the same on interactive pages.

Preview

q3-report.pdf
budget-2026.xlsx
team-photo.jpg

Previewing q3-report.pdf.

Code

RowActions.razor
@inject NavigationManager Navigation

@* A link and a post on each row. Static pages: Delete posts its Name=Value and redirects with the
   deleted names in the address. Interactive pages: OnClick says which button it was. *@
<form method="post" @formname="icon-button-files" @onsubmit="Delete" data-enhance class="ib-grow">
    <AntiforgeryToken />
    <Stack Gap="0" MaxWidth="28.Rem" Grow Border="1" Radius="Radius.Md">
        @foreach (var file in Shown)
        {
            <Row Gap="1" Align="Align.Center" PaddingX="3" PaddingY="2" BorderTop="@(file == Shown[0] ? 0 : 1)">
                <Icon Name="Lucide.FileText" Color="Color.Muted" />
                <TextBlock As="span" Grow Truncate MarginLeft="1">@file</TextBlock>
                <IconButton Icon="Lucide.Eye" Label="@($"Preview {file}")" Href="@Navigation.GetUriWithQueryParameter("preview", file)"
                            Size="ControlSize.Sm" Variant="ButtonVariant.Ghost" Accent="Accent.Neutral" />
                <IconButton Icon="Lucide.Trash" Label="@($"Delete {file}")" Submit Name="delete" Value="@file"
                            OnClick="() => _clicked = file"
                            Size="ControlSize.Sm" Variant="ButtonVariant.Ghost" Accent="Accent.Danger" />
            </Row>
        }
        @if (Shown.Count == 0)
        {
            <TextBlock Size="TextSize.Sm" Color="Color.Muted" Padding="3">No files.</TextBlock>
        }
    </Stack>
</form>
@if (Preview is not null && Shown.Contains(Preview))
{
    <TextBlock Size="TextSize.Sm" Color="Color.Muted" MarginTop="2">Previewing @Preview.</TextBlock>
}
@if (_deleted.Count > 0)
{
    <TextBlock Size="TextSize.Sm" Color="Color.Muted" MarginTop="2">
        Deleted @string.Join(", ", _deleted).
        @if (RendererInfo.IsInteractive)
        {
            <Button Variant="ButtonVariant.Link" Size="ControlSize.Sm" OnClick="() => _deleted.Clear()">Restore</Button>
        }
        else
        {
            <Anchor Href="@Navigation.GetUriWithQueryParameter("deleted", (string?)null)">Restore</Anchor>
        }
    </TextBlock>
}

@code {
    // Static pages: the deleted names live in the address (?deleted=…), so each page load knows them.
    [SupplyParameterFromQuery(Name = "deleted")]
    private string[]? DeletedQuery { get; set; }

    [SupplyParameterFromQuery(Name = "preview")]
    private string? Preview { get; set; }

    // The pressed button's Name=Value posts with the form.
    [SupplyParameterFromForm(FormName = "icon-button-files", Name = "delete")]
    private string? Posted { get; set; }

    // Interactive pages post nothing, so OnClick says which button it was.
    private string? _clicked;
    private List<string> _deleted = [];

    private List<string> Shown => Files.Where(f => !_deleted.Contains(f)).ToList();

    protected override void OnInitialized() => _deleted = [.. DeletedQuery ?? []];

    private void Delete()
    {
        var file = Files.FirstOrDefault(f => f == (Posted ?? _clicked));
        if (file is null || _deleted.Contains(file))
        {
            return;
        }

        _deleted.Add(file);

        // A static post redirects to the new address, so a reload doesn't post again.
        if (!RendererInfo.IsInteractive)
        {
            Navigation.NavigateTo(Navigation.GetUriWithQueryParameters(new Dictionary<string, object?> { ["deleted"] = _deleted.ToArray() }));
        }
    }
}

Off: Delete posts the file's name and the page reloads without it; Restore is a link back.

Put the row in the label too ("Delete budget-2026.xlsx"), so screen readers and the tooltip say which one. OnClick runs only on interactive pages; on static ones the post is what happens, as with Button.

A tooltip on focus

Wrap it in a Tooltip for a hint that shows on keyboard focus as well as hover, or for more than the label, such as a shortcut.

razor
<Tooltip>
    <Content>Bold <Kbd Keys="Ctrl+B" /></Content>
    <ChildContent><IconButton Icon="Lucide.Bold" Label="Bold" Variant="ButtonVariant.Ghost" /></ChildContent>
</Tooltip>

Mode support

What works in each mode, IconButton
FeatureOffStaticLive
Submit or reset a form, follow a link; Name and Value tell buttons apartYesYesYes
Loading and disabled statesYesYesYes
OnClick runs C#NoNoYes

Off: JavaScript disabled. Static: a static SSR page with IzzyBlazor's script. Live: an interactive page (Server, WebAssembly or Auto).

Accessibility

  • It renders a native button, or an a with Href, so Enter, Space and focus work as the browser defines them.
  • Label is required and becomes its aria-label; the icon itself is hidden from screen readers.
  • The tooltip is the native title, so it shows on mouse hover, not on keyboard focus. Inside a Tooltip it has none, and the Tooltip shows on hover and focus.
  • A loading button (not a link) keeps focus and sets aria-busy and aria-disabled; screen readers hear LoadingLabel before the label ("Loading Delete").
  • A disabled or loading link loses its href, keeps role="link" and sets aria-disabled.
  • A toggle (Pressed) sets aria-pressed, so screen readers hear "pressed" or "not pressed"; pressed, it shows an edge in the accent color as well as a tint.

Parameters

IconButton parameters
PropTypeDefault
IconThe icon.IconDataRequired
LabelWhat the button does, for screen readers and the tooltip.stringRequired
HrefRenders a link to this URL instead of a button.string?
SubmitSubmits the form it's in (or Form), when it's a button (not a link), like type="submit".boolfalse
ResetResets the form it's in, when it's a button, like type="reset".boolfalse
NamePosted with the form as Name=Value when this button submits it, so one handler can tell which button was pressed, such as which row's Delete.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 this button submits (native form).string?
AccentPrimary (the default) for the main action, Neutral for everything else, Danger for destructive actions, or a custom color (Accent.Of("#0d9488"), Accent.Token("brand")); text on a custom color is chosen for contrast.AccentAccent.Primary
VariantVisual weight.ButtonVariantButtonVariant.Solid
SizeHeight.ControlSizeControlSize.Md
StartIconAn icon before the text.IconData?
EndIconAn icon after the text.IconData?
FullWidthStretches to the full width of its container.boolfalse
LoadingShows a spinner in place of the start icon and blocks further clicks.boolfalse
LoadingLabelRead to screen readers while Loading.string?"Loading"
DisabledDisables the button.boolfalse
@bind-PressedMakes it a toggle button, such as Bold in an editor: true shows it pressed, false not, and screen readers hear which (aria-pressed).bool?
ChildContentThe label.RenderFragment?
OnClickRuns on click on interactive pages (a link still navigates).EventCallback<MouseEventArgs>

See also