shadcn.io is not affiliated with official shadcn/ui
Shadcn Rating for React and Tailwind
Show reviews and ratings in a visual format.
VVVinicius Vicentini
Tuestday morning at Mercado N.89
Rate your driver
Installation
bunx --bun shadcn@latest add https://kit.dev/r/rating.jsonpnpm dlx shadcn@latest add https://kit.dev/r/rating.jsonnpx shadcn@latest add https://kit.dev/r/rating.jsonyarn shadcn@latest add https://kit.dev/r/rating.jsonInstall the following dependencies:
bun add @ark-ui/react lucide-reactpnpm add @ark-ui/react lucide-reactnpm install @ark-ui/react lucide-reactyarn add @ark-ui/react lucide-reactAdd the following to your globals.css.
:root {
--destructive-foreground: var(--color-red-700);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-700);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-700);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-700);
}
.dark {
--destructive-foreground: var(--color-red-400);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-400);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-400);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-400);
}Copy and paste the following code into your project.
"use client";
import {
RatingGroup as ArkRatingGroup,
useRatingGroupContext,
} from "@ark-ui/react/rating-group";
import { StarIcon } from "lucide-react";
import React from "react";
import { cn } from "@/lib/utils";
export const useRating = useRatingGroupContext;
interface RatingProps extends React.ComponentProps<typeof ArkRatingGroup.Root> {
/**
* The icon to use for the rating.
*
* @default <StarIcon />
*/
icon?: React.ReactNode;
}
export const Rating = (props: RatingProps) => {
const {
icon = <StarIcon />,
allowHalf = false,
count = 5,
className,
...rest
} = props;
return (
<ArkRatingGroup.Root
allowHalf={allowHalf}
className={cn(
"**:data-[slot=rating-item-indicator]:size-6",
"text-warning",
"data-readonly:pointer-events-none",
className
)}
count={count}
data-slot="rating"
{...rest}
>
<ArkRatingGroup.Control
className="inline-flex items-center gap-1"
data-slot="rating-control"
>
<ArkRatingGroup.Context>
{({ items }) =>
items.map((item) => (
<RatingItem index={item} key={item}>
<ArkRatingGroup.ItemContext>
{({ half, highlighted }) => (
<span
className={cn(
"relative inline-flex",
"**:data-fg:text-current **:data-fg:[clip-path:inset(0_0_0_0)]",
"[&[data-half]_[data-fg]]:[clip-path:inset(0_50%_0_0)]",
"[&:not([data-highlighted])_[data-fg]]:[clip-path:inset(0_100%_0_0)]",
"[&_svg]:absolute [&_svg]:inset-0 [&_svg]:size-full [&_svg]:text-current"
)}
data-half={half ? "" : undefined}
data-highlighted={highlighted ? "" : undefined}
data-slot="rating-item-indicator"
>
{React.cloneElement(
icon as React.ReactElement,
{
"data-bg": "",
} as React.ComponentProps<"svg">
)}
{React.cloneElement(
icon as React.ReactElement,
{
"data-fg": "",
fill: "currentColor",
} as React.ComponentProps<"svg">
)}
</span>
)}
</ArkRatingGroup.ItemContext>
</RatingItem>
))
}
</ArkRatingGroup.Context>
<ArkRatingGroup.HiddenInput />
</ArkRatingGroup.Control>
</ArkRatingGroup.Root>
);
};
export const RatingItem = (
props: React.ComponentProps<typeof ArkRatingGroup.Item>
) => {
const { className, ...rest } = props;
return (
<ArkRatingGroup.Item
className={cn(
"inline-flex items-center justify-center",
"rounded-md",
"not-[[data-disabled],[data-readonly]]:cursor-pointer",
"data-disabled:opacity-64 data-disabled:grayscale",
"outline-none focus-visible:ring-current not-data-readonly:focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background",
className
)}
data-slot="rating-item"
{...rest}
/>
);
};Update the import paths to match your project setup.
Anatomy
Rating
├── RatingLabel
├── RatingItem
└── RatingControlUsage
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Rating } from "@/components/ui/rating";<Card className="w-full max-w-xs">
<CardHeader className="flex flex-col items-center gap-3 text-center">
<Avatar size="lg">
<AvatarImage alt="Driver" src="https://github.com/vinihvc.png" />
<AvatarFallback>VV</AvatarFallback>
</Avatar>
<div>
<CardTitle className="text-base">Vinicius Vicentini</CardTitle>
<CardDescription> Tuestday morning at Mercado N.89</CardDescription>
</div>
</CardHeader>
<CardContent className="space-y-2 text-center">
<p className="font-medium text-sm">Rate your driver</p>
<Rating />
</CardContent>
<CardFooter>
<Button className="w-full">Submit</Button>
</CardFooter>
</Card>Accessibility
| Key | Description |
|---|---|
Space | Activate the focused control. |
ArrowLeft | Move left. |
ArrowRight | Move right. |
ArrowUp | Move left. |
ArrowDown | Move right. |
Home | Go to the start. |
End | Go to the end. |
Enter | Activate the focused control. |
Demos
Controlled
Count
Custom Color
Custom Icon
Custom Size
Disabled
Half Star
Readonly
Testimonial
API Reference
Rating
div
PropType
RatingItem
span
PropType
AttributeType
RatingControl
div
PropType