A star rating. As an input it is a radio group of stars; with isReadOnly it is a display like "4.5 out of 5 stars" with an optional count.
You rated 3 out of 5
import { useState } from "react";
import { Rating } from "@rdloom/react";
export default function RatingBasicExample() {
const [value, setValue] = useState(3);
return (
<div className="flex flex-col items-center gap-3 p-6">
<Rating label="Rating" value={value} onChange={setValue} clearable />
<p className="text-sm" aria-live="polite">
{value === 0 ? "No rating yet" : `You rated ${value} out of 5`}
</p>
</div>
);
}Installation
npx rdloom add ratingCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/rating.jsonWorks, but without upgrade tracking.
Usage
import { Rating } from "@rdloom/react";
<div className="flex flex-col items-center gap-3 p-6">
<Rating label="Rating" value={value} onChange={setValue} clearable />
<p className="text-sm" aria-live="polite">
{value === 0 ? "No rating yet" : `You rated ${value} out of 5`}
</p>
</div>Read only with count
import { Rating } from "@rdloom/react";
export default function RatingReadOnlyWithCountExample() {
return (
<div className="flex flex-col items-center gap-3 p-6">
<Rating label="Average rating" value={4.5} allowHalf isReadOnly count={1284} />
<Rating label="Average rating" value={3.8} isReadOnly count={96} countLabel="reviews" size="sm" />
</div>
);
}Half stars
3.5 out of 5
import { useState } from "react";
import { Rating } from "@rdloom/react";
export default function RatingHalfStarsExample() {
const [value, setValue] = useState(3.5);
return (
<div className="flex flex-col items-center gap-3 p-6">
<Rating label="Rating" allowHalf value={value} onChange={setValue} />
<p className="text-sm" aria-live="polite">
{value} out of 5
</p>
</div>
);
}Sizes
import { Rating } from "@rdloom/react";
export default function RatingSizesExample() {
return (
<div className="flex flex-col items-center gap-4 p-6">
<Rating label="Small rating" size="sm" defaultValue={2} />
<Rating label="Medium rating" size="md" defaultValue={3} />
<Rating label="Large rating" size="lg" defaultValue={4} />
</div>
);
}In a form
import { useState } from "react";
import { Form, FormRating, FormSubmitButton, FormTextField } from "@rdloom/react";
export default function RatingInAFormExample() {
const [sent, setSent] = useState<string>();
return (
<div className="flex min-w-0 max-w-full justify-center p-6">
<Form defaultValues={{ comment: "", stars: 0 }} onSubmit={(values) => setSent(`Thanks: ${values.stars} stars`)} className="flex w-80 max-w-full flex-col gap-4">
<FormRating name="stars" label="Rating" isRequired requiredMessage="Choose a rating" />
<FormTextField name="comment" label="Comment" multiline />
<FormSubmitButton>Send review</FormSubmitButton>
<p className="text-sm" aria-live="polite">
{sent ?? ""}
</p>
</Form>
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
labelrequiredName of what is rated, e.g. "Rating" or "Service". Screen readers hear it with the range: "Rating, 1 to 5 stars". | string | none |
valueControlled value. 0 means no rating. | number | none |
defaultValueInitial value when uncontrolled. 0 means no rating. | number | 0 |
onChangeCalled with the new value, 1 to max (halves with allowHalf), or 0 when cleared. | (value: number) => void | none |
maxNumber of stars. | number | 5 |
allowHalfLets people choose half stars, and shows halves when read-only. | boolean | false |
sizeSize of the stars. | "sm" | "md" | "lg" | "md" |
isReadOnlyShows the value as an image with a text alternative instead of an input. | boolean | false |
isDisabledPrevents interaction and dims the stars. | boolean | false |
clearableAdds a Clear button so a chosen rating can be taken back. | boolean | false |
countRead-only only: how many ratings the value comes from, shown as text next to the stars. | number | none |
countLabelThe word shown after the count. | string | "ratings" |
isLabelHiddenHides the visible label. It still names the group for screen readers. | boolean | false |
descriptionHelp text under the stars. | string | none |
errorMessageShown under the stars when isInvalid is true. | string | none |
isInvalidMarks the rating as wrong and shows errorMessage. | boolean | none |
nameSubmits the value with a form under this name. | string | none |
Accessibility
Role radiogroup, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab moves to the stars (the chosen one, or the first)
- Right and Down Arrow choose the next star; Left and Up Arrow choose the previous (in a right-to-left page the horizontal arrows swap)
- With allowHalf each arrow press moves by half a star
- The Clear button is a normal button after the stars
Screen readers announce
- Focusing the group reads "Rating, 1 to 5 stars, radio group" and the chosen star, "4 stars, 4 of 5"
- Arrow keys read the new star as it is chosen
- A read-only rating reads as an image: "4.5 out of 5 stars", then the count text
What your code must do
- The group is a radio group named "<label>, 1 to <max> stars" and each star is a radio named "3 stars" ("3.5 stars" for halves)
- A chosen star is filled and an unchosen one is an outline, so the value never depends on color
- Hovering previews the value; nothing is chosen until a click or key
- Read-only shows role img named "4.5 out of 5 stars"; the count is real text beside it
- Each star's target is at least 24px
Guidelines
Use it when
- Asking for a quick opinion of a product, service or article
- Showing an average rating on a card or a list
Avoid it when
- A precise number: use NumberField or Slider
- A choice with named levels (poor, good, great): use RadioGroup or SegmentedControl
Don't
- Showing a read-only rating with no count or context
- Using only color to tell chosen stars from empty ones
- Making a rating required with no way to take it back
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-action-primary--rd-color-border-strong--rd-color-text-default--rd-color-text-muted--rd-color-feedback-danger--rd-color-focus-ring