Rating React component for Tailwind CSS v4
v1.0.0Added in 1.0.0
A React star rating to rate or show a score with stars or any icon: half values, a hover preview, a label per value and a fill that pops.
Click or tap an icon to rate; with allowHalf the left half of an icon gives a half value. With a mouse the icons preview the rating under the pointer (the part beyond the chosen value dimmed) and the label follows it. Clicking the chosen value again clears it when clearable. On a phone, drag a finger across the icons to scrub and lift it to commit; every icon is a 44px target and the drag never scrolls the page. On commit the icons fill with a small staggered pop, and lowering fades the removed fill. The group is one slider: Tab to it for a focus ring, then Right / Up adds one (a half with allowHalf), Left / Down removes one (mirrored in right-to-left pages), PageUp / PageDown move a whole icon, Home clears (or goes to the lowest value), End jumps to the top, digits 1 to 9 pick a value directly, and 0, Backspace or Delete clear. Read-only ratings are a picture with a spoken summary ("Rated 4.8 of 5"). In the playground Send rating checks that a rating was chosen, pretends to send it and thanks you.
Rating is a Pro React component in the React input components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add rating, together with Field, useControllableState, useLocale, Motion presets, Number format and 2 more and the npm packages motion and lucide-react.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add ratingRating is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import Rating from @/components/ui/rating.
import { useState } from "react"
import { Rating } from "@/components/ui/rating"
const LABELS = ["Very poor", "Poor", "Okay", "Good", "Excellent"]
export function SellerReview() {
const [rating, setRating] = useState(0)
return (
<div className="flex flex-col gap-6">
<Rating
label="How was your experience?"
name="rating"
value={rating}
onValueChange={setRating}
labels={LABELS}
showLabel
emptyLabel="Tap a star"
required
/>
<Rating readOnly value={4.8} tone="warning" size="sm" showValue />
</div>
)
}Props
labeldefault""ReactNodeThe field label, rendered as a span that names the icon group (a click on it focuses the group).
descriptiondefault""ReactNodeHelper text under the icons, linked with aria-describedby. The error takes its place while it shows.
errordefault""ReactNodeReplaces the description with the animated error, tints the empty icons red and sets aria-invalid.
requireddefaultfalsebooleanThe required mark after the label. The check itself belongs to your form (the playground blocks sending without a rating).
invaliddefaultfalsebooleanThe invalid look (red-tinted empty icons) and aria-invalid without a message.
disableddefaultfalsebooleanDims to 50%, leaves the tab order and ignores pointer and keys. A disabled FieldGroup disables it too.
readOnlydefaultfalsebooleanDisplay only: role="img" with a spoken summary, no pointer or keys, and fractions like 4.3 drawn proportionally. The number keeps one decimal ("4,8").
maxdefault5numberHow many icons. Below 1 falls back to 5. The playground keeps its Romanian labels only at 5.
allowHalfdefaultfalsebooleanHalf values: the left half of an icon (right half in RTL) gives x.5, and the arrow keys move by 0.5. Ignored with icons.
clearabledefaulttruebooleanChoosing the current value again clears it, a scrub past the first icon clears, and 0 / Backspace / Delete clear from the keyboard. false keeps at least one step.
sizedefault"md""sm" | "md" | "lg"Icons 16 / 24 / 32px with 2 / 4 / 6px between them, and the value and label type. On touch screens every icon is a 44px target.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"The fill. neutral is black in light mode and white in dark mode; warning gives the classic amber stars.
colordefault""stringAny CSS color for the fill; replaces the tone.
showLabeldefaultfalsebooleanThe hovered or chosen value's label beside the icons (Very poor … Excellent). Needs labels.
emptyLabeldefault""ReactNodeWhat the label slot says while nothing is chosen, with showLabel. Empty shows nothing.
showValuedefaultfalsebooleanThe number beside the icons, through Intl: "4.5" in en-US, "4,5" in de-DE. It follows the hover preview.
localedefault""stringLocale for the number. Empty uses the UIProvider locale, then the nearest LocaleProvider, then the page's language (en-US on the server). The demo passes en-US.
namedefault""stringForm field name: a hidden input submits the value ("0" when empty, "4.5" with halves), and a form reset puts defaultValue back.
icondefault"<Star />"ReactNodeThe icon repeated max times: any lucide icon (Heart, ThumbsUp). The fill layer paints it with fill-current.
iconsReactNode[]One icon per value, e.g. Frown, Meh, Smile, Laugh. Only the chosen (or hovered) icon lights up, in the tone with a soft tone fill; no halves. A list shorter than max repeats its last icon.
labelsstring[]One label per value (labels[0] is 1). Read as aria-valuetext and shown with showLabel.
getValueTextdefault"(v, m) => `${v} of ${m}`"(value: number, max: number) => stringWhat screen readers hear for a value without a label (and for halves).
getReadOnlyLabeldefault"(v, m) => `Rated ${v} of ${m}`"(value: number, max: number) => stringThe spoken name of a read-only rating; the value is rounded to one decimal.
valuenumberThe controlled rating; 0 = none. A value between steps (4.3) is drawn proportionally and snaps on the next interaction.
defaultValuedefault0numberThe uncontrolled start; a form reset returns to it. The playground starts its rating here.
onValueChange(value: number) => voidWhen a rating is committed: on release of a tap or scrub, and on every key press.
idstringThe group's id. Defaults to useId(); the label points at it.
fieldPropsPartial<FieldProps>Anything else for the Field wrapper: optional, labelAction, orientation, labelWidth, hideLabel.
classNamesPartial<Record<"box" | "root" | "item" | "icon" | "fill" | "empty" | "value" | "label" | "field" | "description" | "error", string>>Extra classes for the inner parts: box (icons, number and label), root (the slider group), item (each cell), icon (the icon box), fill and empty (the two layers), value, label and the field parts.
classNamestringClasses for the outermost element: the Field root when the rating has a label, description or error, else the box.
Examples
Read-only in a seller card
A proportional 4.8 in amber with the number beside it, en-US next to ro-RO.
Half values and sizes
allowHalf with the warning tone at sm / md / lg; the three share one value.
Sentiment icons
icons gives one face per value; only the chosen one lights up, with its label.
Custom icon
Any lucide icon through icon: hearts in the danger tone with a label per value.
Required with an error
The empty icons turn red and the error shows until a rating is picked.
Ten-point scale
max={10} with showValue, small icons so the row fits a phone.
Custom color, brand and disabled
A CSS color replaces the tone; disabled dims and ignores input.
Dependencies
Files
- components/ui/rating.tsx
FAQ
Is the React Rating component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Rating?
Run npx wingo-ui@latest add rating. It also adds the Wingo UI items it builds on (Field, useControllableState, useLocale, Motion presets, Number format and 2 more) and the npm packages motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/rating.
How does Rating behave on phones?
On a phone, drag a finger across the icons to scrub and lift it to commit; every icon is a 44px target and the drag never scrolls the page.
See also
Used in
More React input components
Articles using this component
- React Rating Stars: Half Values, Hover and Keyboard
Component guides9 min read