Select
JavaScript off: what the HTML and CSS do on their own.
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 }
}Off: A browser without the customizable select shows each option's text run together ("Pro$12 a month"); the script puts ItemText in its place.
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 }
}Off: The browser stops the post until a plan is chosen, in its own words.
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.