Rating
FreeStars that are a native radio group: arrows change the value, a pointer previews, half steps when you need them, and a read-only average that says how many ratings it comes from.
pnpm dlx shadcn@latest add https://caliperui.site/r/rating.jsonCabin at Lake Bled
When to use #
Use it for
- Reviews and feedback with a clear 1–5 scale.
- Showing a product's average next to its review count.
Skip it when
- Quick thumbs up or down; use the feedback widget.
- Ratings that must be justified; ask a question with radio cards.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/rating.json
Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above.
import { useState } from "react" import { Button } from "@/components/ui/button" import { Rating } from "@/components/ui/rating" const stayWords = ["Poor", "Fair", "Good", "Great", "Loved it"]; export function RatingDemo() { const [stay, setStay] = useState(0); const [cleanliness, setCleanliness] = useState(4.5); const [sent, setSent] = useState(false); return ( <div className="grid w-full max-w-md gap-6"> <div className="flex flex-wrap items-center gap-3 border-b border-border pb-5"> <div className="grid gap-1"> <p className="text-sm font-semibold">Cabin at Lake Bled</p> <Rating label="Average rating" readOnly value={4.62} count={1284} size="sm" /> </div> </div> <form className="grid gap-5" onSubmit={(event) => { event.preventDefault(); setSent(true); }} > <Rating label="How was your stay?" labels={stayWords} value={stay} onValueChange={(value) => { setStay(value); setSent(false); }} name="stay" size="lg" /> <Rating label="Cleanliness" step={0.5} value={cleanliness} onValueChange={setCleanliness} name="cleanliness" /> <Rating label="Check-in (not available for this booking)" defaultValue={3} disabled /> <div className="flex flex-wrap items-center gap-3"> <Button type="submit" disabled={stay === 0}>Send review</Button> <span role="status" className="text-[13px] text-muted-foreground"> {sent ? `Thanks. You rated your stay ${stay} of 5 and cleanliness ${cleanliness} of 5.` : stay === 0 ? "Choose a star to rate your stay." : "Press Delete on a star to clear it."} </span> </div> </form> </div> ); }
API reference #
Rating
component
Accessibility #
It exposes img semantics, with aria-hidden and aria-label kept in step with state.
No custom key handling; Tab and the native activation keys do the work.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.