Rating
Star rating that reads and writes.
Quick Preview
Not rated yet
Usage
import { Rating } from "@giddaa-housing/ui/rating";
// Collects a rating.
<Rating name="satisfaction" defaultValue={4} onValueChange={setRating} />
// Shows one.
<Rating value={4.3} readOnly />An interactive Rating is a group of real radio inputs — one per selectable
value — with the icons drawn over them. That is what gives it native arrow-key
navigation, native form submission under name, and a "3 of 5, radio button"
announcement. A read-only Rating drops the inputs entirely and renders as a
single image, so a reader hears the score once instead of five times.
Examples
Read-only
Leave out the inputs when the score is something to read, not to set. Fractional
values are honoured exactly — 4.3 clips the fifth icon to 30%.
In a form
name puts the value into FormData and groups the radios for the browser.
required is set on the first radio only, so the group reports one validation
message rather than five. Pair it with Field for the label, description, and
error copy, wiring the group up with aria-labelledby and aria-describedby —
a <label htmlFor> cannot point at a radio group.
With a form library, drive it as a controlled field:
<Controller
control={control}
name="satisfaction"
render={({ field, fieldState }) => (
<Rating
aria-invalid={Boolean(fieldState.error)}
aria-labelledby="satisfaction-label"
name={field.name}
onBlur={field.onBlur}
onValueChange={field.onChange}
value={field.value ?? 0}
/>
)}
/>Half stars
precision={0.5} splits each icon into two options, so 3.5 is selectable as
well as displayable. Display is never rounded to precision — a read-only
4.3 renders at 4.3 whatever the precision is.
2.5 of 5
Sizes
size scales the icons and the gap together, and inherits from SizeProvider.
For a size the sm/md/lg scale does not cover, pass iconClassName — it is
merged after the scale, so iconClassName="size-9" wins outright and resizes
both the glyph and the hit area that reserves room for it.
Props
| Prop/API | Type | Default | Description |
|---|---|---|---|
value | number | - | Controlled rating. Fractions render as partially filled icons. |
defaultValue | number | 0 | Starting rating when the component owns its state. |
onValueChange | (value: number) => void | - | Called with the newly picked value. |
max | number | 5 | How many icons to render. |
precision | 1 | 0.5 | 1 | Smallest increment a reader can pick. Display is not rounded to it. |
readOnly | boolean | false | Renders the score as an image instead of a control. |
disabled | boolean | false | Disables every radio and drops the hover preview. |
required | boolean | false | Requires a pick before the form submits. |
name | string | generated | Groups the radios and names the value in submitted form data. |
size | "sm" | "md" | "lg" | { base, md?, lg? } | "md" | Scales the icons and the gap together. Inherits from SizeProvider. |
itemLabel | (value, max) => string | `${value} of ${max}` | Accessible name for each option, and for the read-only image. |
icon | ComponentType<IconProps> | Star | Swaps the glyph. Anything that takes SVG props and paints currentColor. |
iconClassName | string | - | Overrides the icon box for a size outside the scale, e.g. "size-9". Applies to the glyph and its hit area. |
aria-labelledby | string | - | Names the group from an existing label. Takes precedence over aria-label. |
aria-invalid | boolean | - | Marks the group invalid for the surrounding field. |
onBlur | FocusEventHandler | - | Fires when focus leaves the group. For form-library integration. |
className | string | - | Local layout or spacing overrides. |
Accessibility
- Interactive ratings are a
radiogroupof native radios:Tabreaches the group, arrow keys move between and select values, and the value submits with the form. Do not replace them with click handlers. - Name the group. It falls back to
"Rating"; give itaria-labelor pointaria-labelledbyat a visible label whenever the surrounding copy is not enough. - Each option is named
"3 of 5"by default. PassitemLabelwhen the icon is not a star, so the announcement matches what is on screen. - The focus ring is drawn on the hit area rather than the visually hidden input,
so keyboard focus stays visible — including on the half a star
precision={0.5}focuses. - The hover preview is pointer-only and never changes the value; the selected value returns as soon as the pointer leaves.
- Do not communicate the score with color alone. Pair the icons with the numeric value, as the read-only example does.