Rating
An interactive star-rating control with hover preview and any Lucide icon as the shape. Supports read-only, disabled, and form modes.
Playground
<Rating defaultValue={3}>
{Array.from({ length: 5 }).map((_, i) => (
<RatingButton key={i} />
))}
</Rating>Preview
Render one RatingButton per step – buttons index themselves automatically, and hovering previews the value before you click.
<Rating defaultValue={3}>
{Array.from({ length: 5 }).map((_, i) => (
<RatingButton key={i} />
))}
</Rating>Installation
npx shadcn@latest add https://espresso-base-ui.vercel.app/r/rating.jsonUsage
import { Rating, RatingButton } from "@/components/ui/rating"<Rating defaultValue={3} max={5}>
<RatingButton />
<RatingButton />
<RatingButton />
<RatingButton />
<RatingButton />
</Rating>Custom icon and color
Pass any Lucide icon via icon and recolor the filled state through the data-filled attribute – the default filled tint is text-amber-500.
<Rating defaultValue={3}>
{Array.from({ length: 5 }).map((_, i) => (
<RatingButton key={i} icon={Heart} className="data-filled:text-rose-500" />
))}
</Rating>Controlled
Pass value and onValueChange to control the rating from your own state.
const [value, setValue] = React.useState(3)
<Rating value={value} onValueChange={setValue}>
{Array.from({ length: 5 }).map((_, i) => (
<RatingButton key={i} icon={ThumbsUp} className="data-filled:text-blue-500" />
))}
</Rating>Read-only and disabled
readOnly displays a fixed value with no hover or click handling; disabled blocks interaction and dims the whole control to 50% opacity.
<Rating value={4} readOnly>
{Array.from({ length: 5 }).map((_, i) => (
<RatingButton key={i} icon={Zap} className="data-filled:text-amber-400" />
))}
</Rating>
<Rating value={3} disabled>
{Array.from({ length: 5 }).map((_, i) => (
<RatingButton key={i} icon={Sparkles} className="data-filled:text-purple-500" />
))}
</Rating>API reference
Rating renders a div and provides context; RatingButton renders a button. Give the root a name and it renders a hidden input so the value submits with a form. Buttons must be used inside a Rating – they throw otherwise.
Rating
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultValue | number | 0 | Initial value when uncontrolled. |
| value | number | – | Controlled value. |
| onValueChange | (value: number) => void | – | Called with the new value on selection. |
| max | number | 5 | Upper bound used for the accessible label – render one RatingButton per step. |
| size | "xs" | "sm" | "md" | "lg" | "md" | Star size for every button: 16px / 20px / 24px / 28px. |
| readOnly | boolean | false | Display-only mode: role="img" with an accessible "Rated x out of max" label. |
| disabled | boolean | false | Blocks interaction and dims the control to 50% opacity. |
| name | string | – | Renders a hidden input with the current value for form submission. |
| required | boolean | – | Marks the hidden input as required. |
RatingButton
| Prop | Type | Default | Description |
|---|---|---|---|
| index | number | – | 1-based position. Auto-detected from sibling order when omitted. |
| icon | LucideIcon | – | Icon used for both filled and empty states. Defaults to the built-in solid star. |
| filledIcon | LucideIcon | – | Overrides the icon when this step is filled. |
| emptyIcon | LucideIcon | – | Overrides the icon when this step is empty. |
Accessibility & styling hooks
Interactive ratings render as a radiogroup of radio buttons with aria-checked on the selected step and per-button labels like "3 stars"; buttons respond to Enter and Space and preview on focus. In readOnly mode the root becomes role="img" with a summary label. For styling, the root exposes data-slot="rating", data-value, data-size, data-readonly and data-disabled; each button exposes data-slot="rating-button", data-index and data-filled.