rating
kibo-ui-registry/ratingFreeComponent
A star rating component with keyboard navigation and hover effects.
Live preview
live · rendered by the registry
Rendered by Kibo UI Registry on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://www.kibo-ui.com/r/rating.jsonSource
1"use client";23import { useControllableState } from "@radix-ui/react-use-controllable-state";4import { type LucideProps, StarIcon } from "lucide-react";5import type { KeyboardEvent, MouseEvent, ReactElement, ReactNode } from "react";6import {7 Children,8 cloneElement,9 createContext,10 useCallback,11 useContext,12 useEffect,13 useRef,14 useState,15} from "react";16import { cn } from "@/lib/utils";1718type RatingContextValue = {19 value: number;20 readOnly: boolean;21 hoverValue: number | null;22 focusedStar: number | null;23 handleValueChange: (24 event: MouseEvent<HTMLButtonElement> | KeyboardEvent<HTMLButtonElement>,25 value: number26 ) => void;27 handleKeyDown: (event: KeyboardEvent<HTMLButtonElement>) => void;28 setHoverValue: (value: number | null) => void;29 setFocusedStar: (value: number | null) => void;30};3132const RatingContext = createContext<RatingContextValue | null>(null);3334const useRating = () => {35 const context = useContext(RatingContext);36 if (!context) {37 throw new Error("useRating must be used within a Rating component");38 }39 return context;40};4142export type RatingButtonProps = LucideProps & {43 index?: number;44 icon?: ReactElement<LucideProps>;45};4647export const RatingButton = ({48 index: providedIndex,49 size = 20,50 className,51 icon = <StarIcon />,52}: RatingButtonProps) => {53 const {54 value,55 readOnly,56 hoverValue,57 focusedStar,58 handleValueChange,59 handleKeyDown,60 setHoverValue,61 setFocusedStar,62 } = useRating();6364 const index = providedIndex ?? 0;65 const isActive = index < (hoverValue ?? focusedStar ?? value ?? 0);66 let tabIndex = -1;6768 if (!readOnly) {69 tabIndex = value === index + 1 ? 0 : -1;70 }7172 const handleClick = useCallback(73 (event: MouseEvent<HTMLButtonElement>) => {74 handleValueChange(event, index + 1);75 },76 [handleValueChange, index]77 );7879 const handleMouseEnter = useCallback(() => {80 if (!readOnly) {81 setHoverValue(index + 1);82 }83 }, [readOnly, setHoverValue, index]);8485 const handleFocus = useCallback(() => {86 setFocusedStar(index + 1);87 }, [setFocusedStar, index]);8889 const handleBlur = useCallback(() => {90 setFocusedStar(null);91 }, [setFocusedStar]);9293 return (94 <button95 className={cn(96 "rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",97 "p-0.5",98 readOnly && "cursor-default",99 className100 )}101 disabled={readOnly}102 onBlur={handleBlur}103// … truncated
What it pulls in
npm packages
Files it writes
More from Kibo UI Registry
All 41 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| announcementannouncement | Kibo UI Registry | Components | Free | no deps | |
| avatar-stackavatar-stack | Kibo UI Registry | Components | Free | no deps | |
| bannerbanner | Kibo UI Registry | Components | Free | 2 deps | |
| calendarcalendar | Kibo UI Registry | Components | Free | 3 deps | |
| choiceboxchoicebox | Kibo UI Registry | Components | Free | 2 deps | |
| code-blockcode-block | Kibo UI Registry | Components | Free | 5 deps · 2 files | |
| color-pickercolor-picker | Kibo UI Registry | Components | Free | 3 deps | |
| comboboxcombobox | Kibo UI Registry | Components | Free | 2 deps |
