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%.

Adaeze Okonkwo4.8 (126)
Tunde Bello4.3 (54)
Halima Yusuf3.5 (12)

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.

Arrow keys move between stars.

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.

sm
md
lg
9

Props

Prop/APITypeDefaultDescription
valuenumber-Controlled rating. Fractions render as partially filled icons.
defaultValuenumber0Starting rating when the component owns its state.
onValueChange(value: number) => void-Called with the newly picked value.
maxnumber5How many icons to render.
precision1 | 0.51Smallest increment a reader can pick. Display is not rounded to it.
readOnlybooleanfalseRenders the score as an image instead of a control.
disabledbooleanfalseDisables every radio and drops the hover preview.
requiredbooleanfalseRequires a pick before the form submits.
namestringgeneratedGroups 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.
iconComponentType<IconProps>StarSwaps the glyph. Anything that takes SVG props and paints currentColor.
iconClassNamestring-Overrides the icon box for a size outside the scale, e.g. "size-9". Applies to the glyph and its hit area.
aria-labelledbystring-Names the group from an existing label. Takes precedence over aria-label.
aria-invalidboolean-Marks the group invalid for the surrounding field.
onBlurFocusEventHandler-Fires when focus leaves the group. For form-library integration.
classNamestring-Local layout or spacing overrides.

Accessibility

  • Interactive ratings are a radiogroup of native radios: Tab reaches 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 it aria-label or point aria-labelledby at a visible label whenever the surrounding copy is not enough.
  • Each option is named "3 of 5" by default. Pass itemLabel when 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.

On this page