A flexible and customizable React component for creating smooth, interactive custom cursors and enhancements.
- Use any React component
- Smooth cursor movement with configurable smoothing
- Global and Container-specific cursors
- Supports Multiple instances
- Lightweight (<10KB)
- Zero dependencies (except React)
npm install @yhattav/react-component-cursor
or
yarn add @yhattav/react-component-cursor
Note: If you wish to, You'll need to hide the native cursor with CSS (like cursor: none
in the example above). See our styling guide for different approaches.
📖 New to the library? Check out our comprehensive Getting Started Guide for step-by-step tutorials and examples.
import { CustomCursor } from '@yhattav/react-component-cursor';
function App() {
return (
<>
{/* Hide native cursor globally */}
<style>{`body { cursor: none !important; }`}</style>
<CustomCursor>
<div
style={{
width: '20px',
height: '20px',
backgroundColor: '#3b82f6',
borderRadius: '50%',
}}
/>
</CustomCursor>
{/* Your app content */}
</>
);
}
The main component for creating custom cursors.
Prop | Type | Default | Description |
---|---|---|---|
children |
ReactNode |
- | The React component/element to use as cursor content |
enabled |
boolean |
true |
Whether the cursor is enabled and visible |
smoothness |
number |
1 |
Movement smoothing factor (1=instant, higher=smoother) |
containerRef |
RefObject<HTMLElement> |
- | Limit cursor to specific container element |
offset |
CursorOffset |
{ x: 0, y: 0 } |
Pixel offset from mouse position |
centered |
boolean |
true |
Auto-center cursor content on mouse position |
throttleMs |
number |
0 |
Throttle mouse events in milliseconds |
className |
string |
'' |
Additional CSS classes for cursor container |
style |
CSSProperties |
{} |
Additional inline styles for cursor container |
zIndex |
number |
9999 |
CSS z-index for cursor container |
onMove |
CursorMoveHandler |
- | Callback fired on cursor movement |
onVisibilityChange |
CursorVisibilityHandler |
- | Callback fired when cursor visibility changes |
id |
string |
auto-generated | Unique identifier for cursor instance |
showDevIndicator |
boolean |
true |
[Dev Only] Show debug ring in development |
data-testid |
string |
- | Test ID for automated testing |
role |
string |
- | ARIA role for accessibility |
aria-label |
string |
- | ARIA label for accessibility |
The library is written in TypeScript and includes built-in type definitions.
import type {
CustomCursorProps,
CursorPosition,
CursorOffset,
CursorMoveHandler,
CursorVisibilityHandler,
CursorVisibilityReason,
} from '@yhattav/react-component-cursor';
📖 Complete TypeScript Reference →
All prop types, interfaces, and future-ready types with usage examples.
Optional utility functions for advanced SSR scenarios:
import { isSSR, isBrowser, browserOnly, safeDocument, safeWindow } from '@yhattav/react-component-cursor';
Optimized for performance with advanced control for complex use cases.
📖 Complete Performance Guide →
Optimization strategies, settings matrix, and advanced techniques.
Works out of the box with Next.js, Gatsby, Remix, and other SSR frameworks.
// Zero configuration needed - SSR handled automatically
import { CustomCursor } from '@yhattav/react-component-cursor';
<CustomCursor>
<div className="cursor">✨</div>
</CustomCursor>
Live Demo: Interactive Examples & Showcase →
Local Examples (clone and run):
# Vite React example with multiple cursor demos
cd example && npm install && npm run dev
# Next.js example with SSR and advanced patterns
cd example-nextjs && npm install && npm run dev
- ✅ Next.js - Full SSR support with zero configuration
- ✅ Gatsby - Static generation compatible
- ✅ Remix - Server-side rendering works out of the box
- ✅ Vite/CRA - Client-side rendering with optimal performance
- ✅ Astro - Partial hydration compatible
function ContainerExample() {
const containerRef = useRef<HTMLDivElement>(null);
return (
<div
ref={containerRef}
style={{
position: 'relative',
cursor: 'none', // Hide native cursor in this container
}}
>
<CustomCursor containerRef={containerRef} smoothness={2}>
<div
style={{
width: '40px',
height: '40px',
border: '2px solid #ef4444',
borderRadius: '50%',
}}
/>
</CustomCursor>
{/* Container content */}
</div>
);
}
function InteractiveCursor() {
const [isHovered, setIsHovered] = useState(false);
return (
<>
<style>{`body { cursor: none; }`}</style>
<div>
<CustomCursor>
<div
style={{
width: isHovered ? '60px' : '20px',
height: isHovered ? '60px' : '20px',
backgroundColor: '#3b82f6',
borderRadius: '50%',
transition: 'all 0.2s ease',
}}
/>
</CustomCursor>
<button
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
Hover me!
</button>
</div>
</>
);
}
function VisibilityAwareCursor() {
const handleVisibilityChange = (isVisible: boolean, reason: string) => {
console.log('Cursor visibility:', isVisible, 'reason:', reason);
// Reason can be: 'container', 'disabled', or other values in future versions
};
return (
<CustomCursor onVisibilityChange={handleVisibilityChange}>
<div
style={{
width: '20px',
height: '20px',
backgroundColor: '#3b82f6',
borderRadius: '50%',
}}
/>
</CustomCursor>
);
}
We welcome contributions! See our guides:
- Contributing Guide - How to contribute
- Development Guide - Complete development setup and workflow
MIT © Yonatan Hattav