Skip to content

Fields

Rating

A star scale built from hidden native radios, one per step.

When to use it: Use it to collect one score on a small fixed scale, such as one to five stars.

On this page

Example

Loading example…
Rating.tsxtsx
import { useState } from "react";
import { Rating, RatingItem } from "@comp0/react";

export function Example() {
  const [stars, setStars] = useState(3);

  return (
    <div className="flex flex-col gap-1">
      <span className="text-base font-medium text-zinc-900 sm:text-sm dark:text-zinc-100">
        Rate your stay
      </span>
      <Rating className="flex w-fit" name="stay" value={stars} onChange={setStars}>
        {[1, 2, 3, 4, 5].map((star) => (
          <RatingItem
            key={star}
            value={star}
            inputProps={{ "aria-label": `${star} of 5 stars` }}
            className="cursor-pointer rounded px-0.5 text-2xl text-zinc-300 ring-teal-600 transition-colors duration-100 ease-out data-active:text-amber-500 data-focus-visible:ring-2 motion-reduce:transition-none sm:text-xl dark:text-zinc-700 dark:ring-teal-400 dark:data-active:text-amber-400"
          >
            <span aria-hidden="true"></span>
          </RatingItem>
        ))}
      </Rating>
      <p className="text-base text-zinc-600 sm:text-sm dark:text-zinc-400">{stars} of 5 stars</p>
    </div>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. Rating

    Container that names and manages the star radios. Owns a DOM element.

  2. RatingItem

    Star label around one hidden native radio. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Put Rating where the score belongs and give it a name.

  2. 2

    Add the supporting parts

    Add one RatingItem per step with its number as value and a star glyph as children.

  3. 3

    Make the behavior clear

    Style the stars through [data-active], which covers every step up to the hovered or selected one; use readOnly to display a score without allowing changes.

    Exampletsx
    <Rating name="stay" defaultValue={3}>
      <RatingItem value={1} inputProps={{ "aria-label": "1 of 5 stars" }}>
    
      </RatingItem>
    </Rating>;

Keyboard

Moves to and selects the next star.
Moves to and selects the previous star.
Space
Selects the focused star.

Forms and accessibility

The checked radio submits Rating.name and its value.

Accessibility checklist

  • Give each hidden radio a spoken name such as 3 of 5 stars via inputProps.
  • Show the picked score with more than color; the glyphs themselves should fill.
  • Use readOnly for a score people can inspect but not change; it stays focusable.

API reference

Importtsx
import { Rating, RatingItem } from "@comp0/react";

Rating

DOM element

Container that names and manages the star radios.

PropTypeDescription
namestringShared submission name for the item radios; generated when absent.
valuenumberControlled rating; 0 means none.
defaultValuenumberInitial rating; 0 means none.
onChange(value: number) => voidReceives the next rating.
requiredbooleanRequires one item in the group to be selected.
readOnlybooleanKeeps the items focusable while preventing changes.
disabledbooleanDisables every item.

RatingItem

DOM element

Star label around one hidden native radio.

PropTypeDescription
valuenumberThe rating this item stands for; 0.5 steps are allowed.
inputPropsInputHTMLAttributesExtra props for the hidden radio, such as aria-label.

Style hooks

Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.

Rating

Style hookMeaning
[data-disabled]The rating is disabled.
[data-readonly]The rating cannot be changed.

RatingItem

Style hookMeaning
[data-active]This step is at or below the hovered or selected rating.
[data-selected]This step is the exact rating.
[data-focus-visible]Focus should show a visible ring.

Keep exploring