Radio Group
Mutually exclusive selection within a group. Compose MRadioGroup with one or more
MRadioButton items for accessible single-choice inputs.
Overview
The MRadioGroup and MRadioButton components work together to provide
accessible, single-choice selection. MRadioGroup manages shared state through a
cascading context, automatically wiring the name attribute and selection tracking
across all child MRadioButton items. Supports Blazor two-way binding via
@bind-Value and integrates with EditForm for model validation.
Usage
Add the namespace import and include the stylesheet reference if not already done.
@using Marai.UI.Components.MRadioButtonBasic Usage
<MRadioGroup>
<MRadioButton Value="option-a" Label="Option A" />
<MRadioButton Value="option-b" Label="Option B" />
<MRadioButton Value="option-c" Label="Option C" />
</MRadioGroup>
Examples
Default Selected Value
Pass a Value to pre-select an option on render.
<MRadioGroup Value="comfortable">
<MRadioButton Value="default" Label="Default" />
<MRadioButton Value="comfortable" Label="Comfortable" />
<MRadioButton Value="compact" Label="Compact" />
</MRadioGroup>
Two-Way Binding
Use @bind-Value to keep local state in sync with the selection.
Selected: system
<div style="display:flex;flex-direction:column;gap:0.75rem;">
<MRadioGroup @bind-Value="_theme">
<MRadioButton Value="light" Label="Light" />
<MRadioButton Value="dark" Label="Dark" />
<MRadioButton Value="system" Label="System" />
</MRadioGroup>
<p class="text-sm text-muted-foreground">Selected: @_theme</p>
</div>
@code {
private string _theme = "system";
}
Disabled Group
Set Disabled="true" on MRadioGroup to disable all items at once.
<MRadioGroup Value="option-b" Disabled="true">
<MRadioButton Value="option-a" Label="Option A" />
<MRadioButton Value="option-b" Label="Option B" />
<MRadioButton Value="option-c" Label="Option C" />
</MRadioGroup>
Disabled Item
Set Disabled="true" on an individual MRadioButton to disable only that option.
<MRadioGroup Value="option-a">
<MRadioButton Value="option-a" Label="Option A" />
<MRadioButton Value="option-b" Label="Option B (unavailable)" Disabled="true" />
<MRadioButton Value="option-c" Label="Option C" />
</MRadioGroup>
In a Form
Use @bind-Value inside an EditForm for model binding and validation.
<EditForm Model="_model" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<MRadioGroup @bind-Value="_model.Plan">
<MRadioButton Value="free" Label="Free" />
<MRadioButton Value="pro" Label="Pro" />
<MRadioButton Value="enterprise" Label="Enterprise" />
</MRadioGroup>
<ValidationMessage For="() => _model.Plan" />
<MButton Type="submit" Class="mt-4">Continue</MButton>
</EditForm>
@code {
private readonly PlanModel _model = new();
private void HandleSubmit() { /* ... */ }
}
Customization
Custom Tailwind CSS Classes
Use Class on MRadioGroup to control layout and spacing between items,
or on individual MRadioButton items for per-item overrides.
<MRadioGroup Class="gap-4">
<MRadioButton Value="a" Label="Option A" Class="font-semibold" />
<MRadioButton Value="b" Label="Option B" Class="font-semibold" />
<MRadioButton Value="c" Label="Option C" Class="font-semibold" />
</MRadioGroup>
Standard HTML Attributes
Both MRadioGroup and MRadioButton forward arbitrary HTML attributes to their root elements.
Use aria-label on the group for screen readers, and id on individual buttons for precise targeting.
Choose how often you receive updates.
<MRadioGroup aria-label="Notification preferences" aria-describedby="notif-hint">
<MRadioButton Value="all" Label="All notifications" id="notif-all" />
<MRadioButton Value="important" Label="Important only" id="notif-important" />
<MRadioButton Value="none" Label="None" id="notif-none" />
</MRadioGroup>
<p id="notif-hint" class="text-sm text-muted-foreground">Choose how often you receive updates.</p>
- Group spacing:
Class="gap-4"onMRadioGroupfor more space between items - Horizontal layout:
Class="flex-row gap-6"onMRadioGroup - Item styling:
Class="font-semibold"onMRadioButton - Accessibility:
aria-label="..."on the group,aria-describedby="..."on items - Testing:
id="..."ordata-testid="..."on individual buttons
To retheme the radio accent color, override CSS variables in your stylesheet:
:root {
--primary: 222 84% 30%;
--ring: 222 84% 30%;
}Accessibility
MRadioGrouprenders a<div role="radiogroup">— usearia-labeloraria-labelledbyto identify the group for screen readers- The
nameattribute is auto-generated per group instance, so radio buttons are correctly grouped without manual wiring - Each
MRadioButtonrenders a<label>wrapping the input — clicking the label text activates the radio - Disabled items use the
disabledattribute andcursor-not-allowedstyling - Focus ring is applied via
focus-visible:ring-2for keyboard navigation visibility - Arrow keys navigate between options natively — no JavaScript required
API Reference
MRadioGroup
| Parameter | Type | Default | Description |
|---|---|---|---|
| Value | string? | null |
The currently selected value. Use with @bind-Value for two-way binding. |
| ValueChanged | EventCallback<string?> | — |
Callback invoked when the selection changes. Wired automatically by @bind-Value. |
| ValueExpression | Expression<Func<string?>>? | null |
Expression identifying the bound field for use with <ValidationMessage>. |
| Disabled | bool | false |
Disables all child radio buttons in the group. |
| Class | string? | null |
Additional CSS class names appended to the group wrapper element. |
| ChildContent | RenderFragment? | null |
The MRadioButton items to render inside the group. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes passed through to the group <div> (e.g. aria-label, id). |
MRadioButton
| Parameter | Type | Default | Description |
|---|---|---|---|
| Value | string | "" |
The value this radio button represents. Must be unique within the group. |
| Label | string? | null |
Text label rendered next to the radio input. Use ChildContent for richer markup. |
| ChildContent | RenderFragment? | null |
Optional rich label content rendered after the radio input. Ignored if Label is set. |
| Disabled | bool | false |
Disables this individual radio button. Also disabled when the parent group is disabled. |
| Class | string? | null |
Additional CSS class names appended to the item wrapper <label> element. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes passed through to the <input> element (e.g. aria-describedby, id). |
Styling Details
Default Styling
The group renders a vertical flex column and each item uses a label wrapper for click area coverage:
- Group:
flex flex-col gap-3— vertical stack with 12px between items - Item wrapper:
flex items-center gap-3— horizontally aligns input and label with 12px gap - Input:
h-4 w-4 rounded-full— 16px circle with native checked styling viaaccent-primary - Label text:
text-sm font-medium leading-none— compact, readable label - Focus:
focus-visible:ring-2 focus-visible:ring-ring— keyboard focus ring - Disabled:
disabled:cursor-not-allowed disabled:opacity-50on input,peer-disabled:opacity-70on label
- Always provide a
LabelorChildContent— radio buttons without labels are inaccessible - Add
aria-labeltoMRadioGroupwhen there is no visible heading for the group - Use
@bind-Valueinstead of manually managing selection state - Disable individual items via
DisabledonMRadioButtonrather than hiding them - Keep option labels concise — for longer descriptions use
ChildContentwitharia-describedby - Prefer vertical layout (default) for three or more options to reduce scanning effort