AniUI

Rating

Star rating component with interactive and read-only modes. Supports custom max value and sizes.

3 / 5
import { Rating } from "@/components/ui/rating";

const [value, setValue] = useState(3);
<Rating value={value} onChange={setValue} />

Installation

npx @aniui/cli add rating

Sizes

<Rating size="sm" value={3} readOnly />
<Rating size="md" value={3} readOnly />
<Rating size="lg" value={3} readOnly />

Read-Only

Use readOnly to display a non-interactive rating. Use max to customize the number of stars.

<Rating value={4} readOnly />
<Rating value={2} max={10} readOnly />

Props

PropTypeDefault
value
number
max
number
5
onChange
(value: number) => void
readOnly
boolean
false
size
"sm" | "md" | "lg"
"md"
className
string

Source

components/ui/rating.tsx
import React from "react";
import { View, Pressable, Text } from "react-native";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const ratingVariants = cva("flex-row items-center", {
  variants: {
    size: {
      sm: "gap-0.5",
      md: "gap-1",
      lg: "gap-1.5",
    },
  },
  defaultVariants: { size: "md" },
});
const starSizes = { sm: "text-base", md: "text-xl", lg: "text-2xl" } as const;
export interface RatingProps
  extends React.ComponentPropsWithoutRef<typeof View>,
    VariantProps<typeof ratingVariants> {
  className?: string;
  value: number;
  max?: number;
  onChange?: (value: number) => void;
  readOnly?: boolean;
}
export function Rating({ size, className, value, max = 5, onChange, readOnly, ...props }: RatingProps) {
  const s = size ?? "md";
  return (
    <View className={cn(ratingVariants({ size }), className)} accessibilityRole="adjustable" accessibilityValue={{ min: 0, max, now: value }} {...props}>
      {Array.from({ length: max }, (_, i) => {
        const filled = i < value;
        const star = (
          <Text className={cn(starSizes[s], filled ? "text-yellow-400" : "text-muted-foreground/30")}>★</Text>
        );
        return readOnly ? (
          <View key={i}>{star}</View>
        ) : (
          <Pressable key={i} onPress={() => onChange?.(i + 1)} accessible={true} accessibilityRole="button" accessibilityLabel={`${i + 1} star`}>
            {star}
          </Pressable>
        );
      })}
    </View>
  );
}