A flexible and customizable React component for creating smooth, interactive custom cursors. This library allows you to replace the default cursor with any React component, supporting both global and container-specific cursor behaviors.
- π― Use any React component as a cursor
- π Smooth cursor movement with configurable smoothing
- π¦ Container-specific cursors
- π¨ Fully customizable styling
- β‘ Lightweight (<10KB)
- π§ TypeScript support
- π± Zero dependencies (except React)
npm install @yhattav/react-component-cursor
or
yarn add @yhattav/react-component-cursor
import { CustomCursor } from '@yhattav/react-component-cursor';
function App() {
return (
<div style={{ cursor: 'none' }}>
<CustomCursor>
<div
style={{
width: '20px',
height: '20px',
backgroundColor: '#3b82f6',
borderRadius: '50%',
transform: 'translate(-50%, -50%)',
}}
/>
</CustomCursor>
{/ Your app content /}
</div>
);
}
Prop | Type | Default | Description |
---|---|---|---|
children |
ReactNode |
- | The component to use as cursor |
className |
string |
'' |
Additional CSS classes |
style |
CSSProperties |
{} |
Additional inline styles |
offsetX |
number |
0 |
Horizontal offset from cursor position |
offsetY |
number |
0 |
Vertical offset from cursor position |
zIndex |
number |
9999 |
Z-index of the cursor element |
smoothFactor |
number |
1 |
Movement smoothing (1 = no smoothing, higher = smoother) |
containerRef |
RefObject<HTMLElement> |
- | Reference to container element for bounded cursor |
onMove |
(x: number, y: number) => void |
- | Callback fired on cursor movement |
onVisibilityChanged |
(isVisible: boolean) => void |
- | Callback fired when cursor visibility changes |
function ContainerExample() {
const containerRef = useRef<HTMLDivElement>(null);
return (
<div
ref={containerRef}
style={{
position: 'relative',
cursor: 'none',
}}
>
<CustomCursor containerRef={containerRef} smoothFactor={2}>
<div
style={{
width: '40px',
height: '40px',
border: '2px solid #ef4444',
borderRadius: '50%',
transform: 'translate(-50%, -50%)',
}}
/>
</CustomCursor>
{/ Container content /}
</div>
);
}
function InteractiveCursor() {
const [isHovered, setIsHovered] = useState(false);
return (
<div style={{ cursor: 'none' }}>
<CustomCursor>
<div
style={{
width: isHovered ? '60px' : '20px',
height: isHovered ? '60px' : '20px',
backgroundColor: '#3b82f6',
borderRadius: '50%',
transform: 'translate(-50%, -50%)',
transition: 'all 0.2s ease',
}}
/>
</CustomCursor>
<button
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
Hover me!
</button>
</div>
);
}
function VisibilityAwareCursor() {
const handleVisibilityChange = (isVisible: boolean) => {
console.log('Cursor visibility:', isVisible);
};
return (
<CustomCursor onVisibilityChanged={handleVisibilityChange}>
<div
style={{
width: '20px',
height: '20px',
backgroundColor: '#3b82f6',
borderRadius: '50%',
transform: 'translate(-50%, -50%)',
}}
/>
</CustomCursor>
);
}
To start development, you can run both the component and the example app concurrently using:
npm run dev
This project is using tsup for building the library.
This project was bootstrapped with TSDX. Unfortunately, I had to remove the TSDX configuration because it was causing issues with the build process, being unmaintained and not being compatible with the latest versions key packages.
MIT Β© yhattav
The library is written in TypeScript and includes built-in type definitions. No additional @types packages are required.
Example with TypeScript:
import {
CustomCursor,
CustomCursorProps,
} from '@yhattav/react-component-cursor';
// All props are properly typed
const MyComponent: React.FC = () => {
const containerRef = useRef<HTMLDivElement>(null);
return (
<CustomCursor
containerRef={containerRef}
smoothFactor={2}
onMove={(x: number, y: number) => console.log(x, y)}
onVisibilityChanged={(isVisible: boolean) =>
console.log('Visible:', isVisible)
}
>
{/* Your cursor content */}
</CustomCursor>
);
};
The library is lightweight (<10KB) and is monitored using size-limit. You can check the current bundle size by running:
npm run size
We welcome contributions! Please see our Contributing Guide for details.
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
npm run test
We maintain 100% test coverage for all new features. Please ensure your contributions include appropriate tests.
We follow the Conventional Commits specification. This helps us automatically generate changelogs and determine semantic version bumps.
Examples:
- feat: add new cursor animation option
- fix: resolve smoothing calculation issue
- docs: update installation instructions
- test: add tests for container bounds
Please use our issue templates when creating new issues:
- Bug Report
- Feature Request
- Documentation Update