Rating

Rating

An interactive star-rating control with hover preview and any Lucide icon as the shape. Supports read-only, disabled, and form modes.

Playground

value
readOnly
disabled
<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.json

Usage

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.

Rated 3 of 5
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

PropTypeDefaultDescription
defaultValuenumber0Initial value when uncontrolled.
valuenumber–Controlled value.
onValueChange(value: number) => void–Called with the new value on selection.
maxnumber5Upper 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.
readOnlybooleanfalseDisplay-only mode: role="img" with an accessible "Rated x out of max" label.
disabledbooleanfalseBlocks interaction and dims the control to 50% opacity.
namestring–Renders a hidden input with the current value for form submission.
requiredboolean–Marks the hidden input as required.

RatingButton

PropTypeDefaultDescription
indexnumber–1-based position. Auto-detected from sibling order when omitted.
iconLucideIcon–Icon used for both filled and empty states. Defaults to the built-in solid star.
filledIconLucideIcon–Overrides the icon when this step is filled.
emptyIconLucideIcon–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.

Search documentation

Search the component docs