RadioGroup
One choice from a short list of native radios, in a fieldset with a legend. The shell is server-rendered; one small client part passes the group's name and value to each radio.
Import
import { Radio, RadioGroup } from '@nizivo/react/radio-group';
// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css and @nizivo/react/css/fieldset.css and @nizivo/react/css/inline-error.css
import '@nizivo/react/css/radio-group.css';A required RadioGroup keeps the browser's own validation bubble; it does not show an inline message
or set aria-invalid until you pass error. Mixing it with Field-wired controls in one
native-validated form is covered on Field.
Static or interactive
Client. The fieldset, legend, description and error are server HTML, and the radios' client leaf always ships with them. Native radios that share a name keep arrow keys and one checked radio before hydration. How to choose.
Examples
Basic
Native radios sharing one name: arrow keys move the choice, and the form submits it, with no JS.
import type { JSX } from 'react';
import { Radio, RadioGroup } from '@nizivo/react/radio-group';
export function RadioGroupExample(): JSX.Element {
return (
<RadioGroup legend="Shipping" name="shipping" defaultValue="standard">
<Radio value="standard">Standard shipping</Radio>
<Radio value="express">Express shipping</Radio>
<Radio value="pickup" disabled>
Store pickup (no stores yet)
</Radio>
</RadioGroup>
);
}
Horizontal
Horizontal, with a description.
import type { JSX } from 'react';
import { Radio, RadioGroup } from '@nizivo/react/radio-group';
export function RadioGroupHorizontalExample(): JSX.Element {
return (
<RadioGroup
legend="Weight unit"
description="Used on packing slips."
name="unit"
defaultValue="kg"
orientation="horizontal"
>
<Radio value="kg">Kilograms</Radio>
<Radio value="lb">Pounds</Radio>
</RadioGroup>
);
}
Error
Required, with an error from the server: aria-invalid and the message on the group.
import type { JSX } from 'react';
import { Radio, RadioGroup } from '@nizivo/react/radio-group';
export function RadioGroupErrorExample(): JSX.Element {
return (
<RadioGroup legend="Tax category" name="tax" required error="Choose a tax category.">
<Radio value="standard">Standard rate</Radio>
<Radio value="reduced">Reduced rate</Radio>
<Radio value="exempt">Exempt</Radio>
</RadioGroup>
);
}
Disabled
The whole group disabled: the native fieldset disables every radio, with no client code.
import type { JSX } from 'react';
import { Radio, RadioGroup } from '@nizivo/react/radio-group';
export function RadioGroupDisabledExample(): JSX.Element {
return (
<RadioGroup legend="Fulfilment" name="fulfilment" defaultValue="manual" disabled>
<Radio value="manual">Manual</Radio>
<Radio value="automatic">Automatic</Radio>
</RadioGroup>
);
}
Props
Radio
One option of a RadioGroup. className and style go on the root <label>; every other attribute, ref included, goes on the native <input>. Name, checked state and required come from the group.
Client component Renders data-slot="radio" with the class nz-radio. Props: RadioProps. Source: src/radio-group/radio-group.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | – | The label, after the circle. |
render | RenderProp<RadioState> | – | Composition of the root |
valuerequired | string | – | Submitted under the group's name when this radio is checked. |
- Also takes every attribute of
<input>exceptchecked,children,defaultChecked,name,readOnly,required,size,type,value. (Omit<ComponentPropsWithRef<"input">, "type" | "value" | "name" | "checked" | "defaultChecked" | "required" | "readOnly" | "children" | "size">)
render takes an element, or, in client code, a function of the props and this state (RadioState):
| Name | Type | Default | Description |
|---|---|---|---|
disabledrequired | boolean | – |
RadioGroup
A native <fieldset role="radiogroup"> with a <legend> (Fieldset), plus aria-required and aria-invalid, which role group does not allow but radiogroup does (FIELD-CONTRACT.md). required marks the legend and makes every radio required; disabled is the fieldset's own.
Server component Renders data-slot="radio-group" with the class nz-radio-group. Props: RadioGroupProps. Source: src/radio-group/radio-group.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | – |
|
defaultValue | string | – | Uncontrolled initial value. |
description | ReactNode | – | Help text after the group, announced on entering it through From |
disabled | boolean | false | Native: disables every control inside, with no client code. From |
error | ReactNode | – | The group's error, rendered through InlineError and joined to From |
errorIcon | ReactNode | – | Replaces the internal error mark ( From |
ids | {
description?: string | undefined;
error?: string | undefined;
} | – | Ids replacing the generated ones. From |
legend | ReactNode | – | The group's name, a native From |
name | string | a generated name, unique per group | The name every radio submits under. |
onValueChange | (value: string, details: ChangeDetails<RadioGroupChangeReason>) => void | – | Called with the newly checked radio's |
orientation | Orientation"horizontal""vertical" | 'vertical' | Layout of the radios; native arrow keys move both ways either way. |
render | RenderProp<FieldsetState> | – | Composition of the root; anything but a From |
required | boolean | false | Draws an From |
value | string | null | – | Controlled: the checked radio's |
- Also takes every attribute of
<fieldset>exceptchildren,defaultValue,name,onChange,role;disabledis redeclared above. (Omit<ComponentPropsWithRef<"fieldset">, "children">)
render takes an element, or, in client code, a function of the props and this state (FieldsetState):
| Name | Type | Default | Description |
|---|---|---|---|
disabledrequired | boolean | – | |
invalidrequired | boolean | – | An error message is presented. |
requiredrequired | boolean | – |
Other exports
RadioGroupChangeReasontypeimport type { RadioGroupChangeReason } from '@nizivo/react/radio-group'Every change comes from the native
changeevent of the radio that became checked.type RadioGroupChangeReason = "input"RadioGroupPropstypeimport type { RadioGroupProps } from '@nizivo/react/radio-group'The props of
RadioGroup, listed above.RadioPropstypeimport type { RadioProps } from '@nizivo/react/radio-group'The props of
Radio, listed above.RadioStatetypeimport type { RadioState } from '@nizivo/react/radio-group'What a
renderfunction receives for the root<label>.The render state of
Radio, listed above.
Data attributes and slots
Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.
| Slot | Rendered by | Data attributes |
|---|---|---|
radio.nz-radio | Radioroot (takes render) |
|
radio-group.nz-radio-group | RadioGrouproot (takes render) |
|
radio-input.nz-radio-input | Radioinner element (CSS only); interactive, focus: self; forced colours: border, fill on :checked | none |
radio-label.nz-radio-label | Radioinner element (CSS only) | none |
CSS hooks
Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.
| Hook | Default | Read as |
|---|---|---|
--nz-radio-accent | --nz-bg-brand |
|
--nz-radio-gap | --nz-space-2 |
|
--nz-radio-group-gap | --nz-space-6 |
|
--nz-radio-size |
|
|
Layers: nz.components. Classes: .nz-fieldset-content, .nz-fieldset-description, .nz-fieldset-error, .nz-radio, .nz-radio-group, .nz-radio-input, .nz-radio-label.
Tokens this stylesheet reads (18)
--nz-bg-brand, --nz-bg-disabled, --nz-bg-muted, --nz-bg-surface, --nz-border-control, --nz-border-control-hover, --nz-border-critical, --nz-border-subtle, --nz-border-width-default, --nz-duration-fast, --nz-easing-standard, --nz-font-body-m, --nz-radius-full, --nz-space-2, --nz-space-6, --nz-text-disabled, --nz-text-on-brand, --nz-text-primary
Accessibility
These parts are interactive. Each gets the focus outline and touch-action rule from @nizivo/react/css/base.css (the nz.base layer), so the floor survives a restyle:
radio-input: draws the focus outline itself on keyboard focus.
In forced-colours mode these states keep a visible system colour: radio-input (border), radio-input:checked (fill).
Pattern: WAI-ARIA Authoring Practices.
Keyboard
| Keys | Action |
|---|---|
| Tab | Moves focus into the group, to the checked radio, or to the first one when none is checked; the next Tab leaves the group. |
| ArrowDown, ArrowRight | Checks the next radio and focuses it, skipping disabled ones. |
| ArrowUp, ArrowLeft | Checks the previous radio and focuses it, skipping disabled ones. |
| Space | Checks the focused radio when none is checked. |
Source: native <input type="radio"> elements sharing one name (radio-group.client.tsx), which add no key handling; the browser lane (test/browser/toggles.spec.tsx) checks ArrowDown.
Figma
Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.
- Canonical: Figma node
7275:11479 - Ghost masters: none
QuantityStepper
A whole-number quantity, such as a product page's or a cart line's, in Input's box between a minus and a plus button. The input is a native number input; the buttons step it for the pointer and are disabled at the ends of the range.
RankedList
Top products, channels or regions as an ordered list, each row a label, its value and a bar relative to the leader. The bars are decorative; the values are printed.