Rating
Interactive or read-only star rating with half stars.
$
pnpm dlx shadcn@latest add https://lodeui.com/r/rating.jsonYou rated 4 of 5
4.5 average · 1,284 reviews
components/examples/rating-demo.tsx
"use client";
import * as React from "react";
import { Rating } from "@/components/ui/rating";
export default function RatingDemo() {
const [v, setV] = React.useState(4);
return (
<div className="flex flex-col items-center gap-4">
<Rating value={v} onValueChange={setV} size={28} aria-label="Your rating" />
<p className="text-sm text-muted-foreground">You rated {v} of 5</p>
<div className="flex items-center gap-2 text-sm"><Rating value={4.5} readOnly size={16} /><span className="text-muted-foreground">4.5 average · 1,284 reviews</span></div>
</div>
);
}When to use
- Collecting or showing star ratings.
When not to use
- Precise satisfaction scores: use a radio group with labels.
Installation
$
pnpm dlx shadcn@latest add https://lodeui.com/r/rating.json1Install the dependencies
npm install motion lucide-react2Copy the source into your project
components/ui/rating.tsx"use client"; import * as React from "react"; import { motion } from "motion/react"; import { Star } from "lucide-react"; import { cn } from "@/lib/utils"; export interface RatingProps { value: number; onValueChange?: (v: number) => void; max?: number; size?: number; /** Show half stars for read-only averages like 4.5. */ readOnly?: boolean; className?: string; "aria-label"?: string; } export function Rating({ value, onValueChange, max = 5, size = 18, readOnly, className, ...aria }: RatingProps) { const [hover, setHover] = React.useState<number | null>(null); const shown = hover ?? value; const interactive = !readOnly && !!onValueChange; return ( <div role={interactive ? "radiogroup" : "img"} aria-label={aria["aria-label"] ?? `${value} out of ${max} stars`} className={cn("inline-flex items-center gap-0.5", className)} onPointerLeave={() => setHover(null)} > {Array.from({ length: max }, (_, i) => { const fill = Math.max(0, Math.min(1, shown - i)); const star = ( <span className="relative inline-block" style={{ width: size, height: size }}> <Star className="absolute inset-0 text-muted-foreground/30" style={{ width: size, height: size }} /> <span className="absolute inset-0 overflow-hidden" style={{ width: `${fill * 100}%` }}> <Star className="fill-amber-400 text-amber-400" style={{ width: size, height: size }} /> </span> </span> ); return interactive ? ( <motion.button key={i} type="button" role="radio" aria-checked={Math.round(value) === i + 1} aria-label={`${i + 1} star${i ? "s" : ""}`} whileTap={{ scale: 0.85 }} onPointerEnter={() => setHover(i + 1)} onClick={() => onValueChange(i + 1)} onKeyDown={(e) => { if (e.key === "ArrowRight") onValueChange(Math.min(max, value + 1)); if (e.key === "ArrowLeft") onValueChange(Math.max(1, value - 1)); }} className="rounded outline-none focus-visible:ring-2 focus-visible:ring-ring" > {star} </motion.button> ) : ( <span key={i}>{star}</span> ); })} </div> ); }
Usage
Import it from @/components/ui/rating after installing.
example.tsx
import * as React from "react";
import { Rating } from "@/components/ui/rating";
export function Review() {
const [stars, setStars] = React.useState(0);
return <Rating aria-label="Rating" value={stars} onValueChange={setStars} />;
}API reference
Rating
Interactive stars, or read-only with half stars.
| Prop | Type | Default | Description |
|---|---|---|---|
| value / onValueChange | number | – | Current rating. |
| max | number | 5 | Stars. |
| size | number | 18 | Star size in px. |
| readOnly | boolean | – | Display only; supports halves. |
Keyboard interactions
| Keys | Action |
|---|---|
| ←or→ | Change rating |
Accessibility
- Interactive stars are a radiogroup; each star is labelled.
Motion
- Stars pop on hover and select.
Responsive behavior
- Fixed size; use 24px+ for touch.
Performance
- Static markup.
Notes for AI
- Use readOnly for averages like 4.6.
This page as Markdown, for assistants and coding agents:
/components/rating/markdownThe full library index for assistants is at /llms.txt.
Lode222 doc pagesllms.txt