Cursor Glow
Ambient glow layer that follows the pointer across its container and fades when it leaves. Drop it behind heroes, grids or feature panels.
Example
Installation
npx shadcn@latest add https://hirael.com/r/cursor-glow.jsonUsage
import { CursorGlow } from "@/components/cursor-glow"Paths assume the default @/ alias from your components.json. The example above shows the parts composed.
API
<CursorGlow />
+ native element props| Prop | Type | Default |
|---|---|---|
sizeDiameter of the glow, in px. | number | 400 |
colorGlow color. Defaults to a soft tint of the foreground token. | string | 'color-mix(in oklch, var(--foreground) 12%, transparent)' |
blurSoften the glow's edge. Turn off for a crisper spotlight. | boolean | true |
Component source
'use client';
import * as React from 'react';
import { motion, useMotionTemplate, useMotionValue, useReducedMotion } from 'motion/react';
import { cn } from '@/lib/utils';
export interface CursorGlowProps extends React.ComponentProps<'div'> {
/** Diameter of the glow, in px. */
size?: number;
/** Glow color. Defaults to a soft tint of the foreground token. */
color?: string;
/** Soften the glow's edge. Turn off for a crisper spotlight. */
blur?: boolean;
}
const CursorGlow = ({
className,
children,
size = 400,
color = 'color-mix(in oklch, var(--foreground) 12%, transparent)',
blur = true,
onPointerMove: onPointerMoveProp,
...props
}: CursorGlowProps) => {
const reduced = useReducedMotion();
const x = useMotionValue(0);
const y = useMotionValue(0);
const background = useMotionTemplate`radial-gradient(${size}px circle at ${x}px ${y}px, ${color}, transparent 70%)`;
const onPointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
onPointerMoveProp?.(event);
if (event.defaultPrevented || reduced || event.pointerType !== 'mouse') return;
const rect = event.currentTarget.getBoundingClientRect();
x.set(event.clientX - rect.left);
y.set(event.clientY - rect.top);
};
return (
<div
data-slot="cursor-glow"
className={cn('group relative overflow-hidden', className)}
{...props}
onPointerMove={onPointerMove}
>
{/* The layer never tracks the pointer under reduced motion, so it would sit pinned at 0,0. */}
{reduced ? null : (
<motion.div
aria-hidden
data-slot="cursor-glow-layer"
className={cn(
'pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-500 group-hover:opacity-100',
blur && 'blur-2xl',
)}
style={{ background }}
/>
)}
<div data-slot="cursor-glow-content" className="relative">
{children}
</div>
</div>
);
};
export { CursorGlow };
Dependencies
npm
motion