Custom Cursor
sora-ui/custom-cursorFreeComponent
A smooth custom cursor that morphs when hovering interactive targets, powered by Motion.
Install it
npx shadcn@latest add https://ui.soralabs.io.vn/r/custom-cursor.jsonSource
1/** biome-ignore-all lint/a11y/noNoninteractiveElementInteractions: Cursor targets use hover-only affordances. */2/** biome-ignore-all lint/a11y/noStaticElementInteractions: Cursor targets are decorative hover zones. */34"use client";56import { cn } from "@/lib/utils";7import { cva, type VariantProps } from "class-variance-authority";8import {9 motion,10 type SpringOptions,11 useMotionValue,12 useReducedMotion,13 useSpring,14} from "motion/react";15import {16 type ComponentProps,17 type ComponentPropsWithoutRef,18 createContext,19 type ReactNode,20 useContext,21 useEffect,22 useMemo,23 useState,24} from "react";2526type MotionDivAnimate = NonNullable<27 ComponentProps<typeof motion.div>["animate"]28>;2930const DEFAULT_CURSOR_COLOR = "#ff4c24";3132const CURSOR_EASE = [0.625, 0.05, 0, 1] as const;3334const customCursorVariants = cva("relative select-none", {35 variants: {36 layout: {37 default: "",38 demo: "flex min-h-72 items-center justify-center",39 },40 },41 defaultVariants: {42 layout: "default",43 },44});4546const customCursorTargetVariants = cva(47 "flex items-center justify-center text-foreground transition-opacity hover:opacity-80",48 {49 variants: {50 size: {51 sm: "size-10",52 md: "size-14",53 lg: "size-16",54 },55 },56 defaultVariants: {57 size: "md",58 },59 }60);6162interface CustomCursorContextValue {63 setIsHovering: (isHovering: boolean) => void;64}6566const CustomCursorContext = createContext<CustomCursorContextValue | null>(67 null68);6970function useCustomCursorContext() {71 const context = useContext(CustomCursorContext);7273 if (!context) {74 throw new Error(75 "CustomCursorTarget must be used within a CustomCursor provider."76 );77 }7879 return context;80}8182function hexToRgba(hex: string, alpha: number) {83 const normalized = hex.replace("#", "");8485 if (normalized.length !== 6) {86 return `rgba(255, 76, 36, ${alpha})`;87 }8889 const red = Number.parseInt(normalized.slice(0, 2), 16);90 const green = Number.parseInt(normalized.slice(2, 4), 16);91 const blue = Number.parseInt(normalized.slice(4, 6), 16);9293 return `rgba(${red}, ${green}, ${blue}, ${alpha})`;94}9596function resolveCursorAppearance(97 isHovering: boolean,98 color: string99): MotionDivAnimate {100 if (!isHovering) {101 return {102 width: 16,103 height: 16,104 borderRadius: 9999,105 backgroundColor: color,106 borderColor: color,107 borderWidth: 1,108 };109 }110111 return {112 width: 48,113 height: 48,114 borderRadius: 9999,115// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from Sora UI
All 97 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | Sora UI | Components | Free | 2 deps | |
| Border Trailborder-trail | Sora UI | Components | Free | 1 dep | |
| Bottom Sheetbottom-sheet | Sora UI | Components | Free | 2 deps | |
| Cursor Bubblecursor-bubble | Sora UI | Components | Free | 2 deps | |
| Cursor Trail Revealcursor-trail-reveal | Sora UI | Components | Free | 1 dep | |
| Accordion Demodemo-accordion | Sora UI | Components | Free | no deps | |
| Border Trail Demodemo-border-trail | Sora UI | Components | Free | no deps | |
| Border Trail Loading Card Demodemo-border-trail-loading | Sora UI | Components | Free | no deps |
