Select
An interactive page: C# runs over a Blazor Server connection.
Usage
@* An enum: its values are the options. *@
<Select @bind-Value="Model!.Plan" />
@* Your own list, with the text for each. *@
<Select @bind-Value="Model!.Country" Items="CountryCodes" ItemText="code => CountryNames[code!]"
Placeholder="Choose a country" />Options
Bind an enum and its values are the options, named by their [Display(Name)]. For anything else, pass Items and the ItemText for each. Placeholder adds a first, empty option.
Preview
Code
System.ComponentModel.DataAnnotations
<Form Model="Model" FormName="options" MaxWidth="24.Rem" Grow>
@* An enum: its values are the options, named by [Display(Name)]. *@
<Select @bind-Value="Model!.Plan" />
@* A list of your own, with the text for each. *@
<Select @bind-Value="Model!.Country" Items="Countries.Keys" ItemText="code => Countries[code!]"
Placeholder="Choose a country" />
</Form>
{
[SupplyParameterFromForm(FormName = "options")]
private Signup? Model { get; set; }
private static readonly Dictionary<string, string> Countries = new()
{
["CA"] = "Canada", ["MX"] = "Mexico", ["GB"] = "United Kingdom", ["US"] = "United States",
};
protected override void OnInitialized() => Model ??= new();
public sealed class Signup
{
public Plan Plan { get; set; } = Plan.Pro;
// A country code: "CA", "MX", ...
public string? Country { get; set; }
}
public enum Plan
{
Free,
Pro,
[Display(Name = "Team (5 seats)")] Team,
}
}The label and required mark come from the model as they do for a TextField: [Display(Name)] or the property name in words, and [Required]. Label replaces the label, and Hint adds help under the field.
Rich options
Use ItemTemplate for markup in each option, such as an icon and a price. Text with the class ib-option-hint shows in the list but not in the closed field.
Preview
Code
<Form Model="Model" FormName="rich" MaxWidth="24.Rem" Grow>
<Select @bind-Value="Model!.Plan" ItemText="plan => plan.ToString()">
<ItemTemplate>
<Icon Name="Icons[context]" />
<span>context</span>
<small class="ib-option-hint">Prices[context]</small>
</ItemTemplate>
</Select>
</Form>
{
[SupplyParameterFromForm(FormName = "rich")]
private Signup? Model { get; set; }
private static readonly Dictionary<Plan, IconData> Icons = new()
{
[Plan.Free] = Lucide.Leaf, [Plan.Pro] = Lucide.Rocket, [Plan.Team] = Lucide.Users,
};
private static readonly Dictionary<Plan, string> Prices = new()
{
[Plan.Free] = "$0", [Plan.Pro] = "$12 a month", [Plan.Team] = "$40 a month",
};
protected override void OnInitialized() => Model ??= new();
public sealed class Signup
{
public Plan Plan { get; set; } = Plan.Pro;
}
public enum Plan { Free, Pro, Team }
}The markup shows where the browser has the customizable select (Chrome and Edge 135, Safari 27). Elsewhere IzzyBlazor's script shows ItemText in the browser's own list, so keep both; with JavaScript off those browsers show the markup's text run together.
Required, with a placeholder
Use a nullable property with [Required] and a Placeholder: nothing is chosen until someone picks an option, and the placeholder can't be picked again.
Preview
Code
System.ComponentModel.DataAnnotations
<Form Model="Model" FormName="required" OnValidSubmit="Save" MaxWidth="24.Rem" Grow>
<Select @bind-Value="Model!.Plan" Placeholder="Choose a plan" />
<Row><Button Submit>Continue</Button></Row>
if (_saved)
{
<Alert Status="Status.Success">You chose Model!.Plan.</Alert>
}
</Form>
{
[SupplyParameterFromForm(FormName = "required")]
private Signup? Model { get; set; }
private bool _saved;
protected override void OnInitialized() => Model ??= new();
private void Save() => _saved = true;
public sealed class Signup
{
// Nullable, so nothing is chosen until someone picks one.
[Required(ErrorMessage = "Choose a plan")]
public Plan? Plan { get; set; }
}
public enum Plan { Free, Pro, Team }
}Live: The browser stops the post in the model's words, as on static pages, and Continue runs C# without a page load.
Sizes, read-only and disabled
Use Size to line up with buttons and other fields, ReadOnly for a value that posts but can't change, and Disabled for one that doesn't post.
Preview
Code
<Form Model="Model" FormName="select-states" MaxWidth="24.Rem" Grow>
<Select @bind-Value="Model!.Small" Label="Small" Size="ControlSize.Sm" />
<Select @bind-Value="Model!.Large" Label="Large" Size="ControlSize.Lg" />
<Select @bind-Value="Model!.Region" Label="Region" ReadOnly Hint="Read-only: posted, but can't be changed." />
<Select @bind-Value="Model!.Backup" Label="Backup region" Disabled Hint="Disabled: not posted." />
</Form>
{
[SupplyParameterFromForm(FormName = "select-states")]
private Settings? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
public sealed class Settings
{
public Region Small { get; set; }
public Region Large { get; set; }
public Region Region { get; set; } = Region.Europe;
public Region Backup { get; set; } = Region.Asia;
}
public enum Region { Americas, Europe, Asia }
}HTML has no read-only select, so a read-only one shows its choice in a read-only text field and posts it from a hidden input.
Filtering a page
Use a FilterSelect in a FilterForm to filter a list as soon as someone picks, since a select doesn't post when it changes: it applies on change with the script and has an Apply button without it. On an interactive page, @bind-Value:after runs C# after each change.
@* Any page: sets ?status= *@
<FilterForm>
<FilterSelect Name="status" Label="Status" EnumType="typeof(OrderStatus)" />
</FilterForm>
@* Interactive page: C# after each change. *@
<Select @bind-Value="Model!.Status" @bind-Value:after="ReloadAsync" />Mode support
| Feature | Off | Static | Live |
|---|---|---|---|
| Everything | Yes | Yes | Yes |
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
selectwith alabellinked to it, so the keyboard, typing to jump to an option and the screen reader's announcements are the browser's own. - The hint and, when the field is invalid, its errors are linked with
aria-describedby; an invalid field setsaria-invalid. - A read-only Select shows its value in a read-only text field, which can be focused and is announced as read-only, where a disabled select would be skipped.
- The chosen text's hover tooltip is on the wrapper, not the select, so screen readers don't hear the value twice.
- With
ItemTemplate, keepItemText: browsers without the customizable select show and read it in place of the markup.
Parameters
| Prop | Type | Default |
|---|---|---|
ReadOnlyMakes the field read-only: the chosen text shows in a read-only text field, and the value still posts. | bool | false |
ItemsThe options. | IEnumerable<TValue>? | |
ItemTextThe text for each option. | Func<TValue, string>? | computed |
ItemTemplateMarkup for each option, such as an icon, avatar or a second line (class ib-option-hint, hidden in the closed field). | RenderFragment<TValue>? | |
PlaceholderA first, empty option such as "Choose a plan". | string? | |
@bind-ValueThe value, bound with @bind-Value. | TValue | |
DisplayNameThe field's name in validation messages from the input itself, such as a value that can't be parsed. | string? |
Plus 26 field props.
Full APISee also
- Combobox: type to filter; server search on interactive pages.
- RadioGroup: one choice from a list of radios.
- SegmentedControl: one choice from a few, as joined buttons.
- MultiSelect: several choices as chips you can type to filter.