A lightweight and highly customizable React component for creating unique and interactive cursor experiences.
npm install @phazr/custom-cursor

A lightweight and highly customizable React component for creating unique and interactive cursor experiences with full SSR support.
- ✅ SSR Compatible - Works seamlessly with Next.js, Remix, Gatsby, and other SSR frameworks
- ✅ TypeScript Support - Full type safety out of the box
- ✅ Customizable - Define your own cursor variants and styles
- ✅ Smooth Animations - Powered by Framer Motion
- ✅ Zero Config - Works out of the box with sensible defaults
- ✅ Accessibility - Automatically hides on touch devices and respects user preferences
- ✅ Lightweight - Minimal bundle size impact
``bash`
npm install @phazr/custom-cursor motionor
yarn add @phazr/custom-cursor motionor
pnpm add @phazr/custom-cursor motion
`tsx`
import { CursorProvider, Cursor } from '@phazr/custom-cursor';
// Make sure import css also unless its wont work as expected
import '@phazr/custom-cursor/cursor.css';
function App() {
return (
Your app content
);
}
`tsx`
// app/layout.tsx
import { CursorProvider, Cursor } from '@phazr/custom-cursor';
// Make sure import css also unless its wont work as expected
import '@phazr/custom-cursor/cursor.css';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
`tsx
// pages/_app.tsx
import { CursorProvider, Cursor } from '@phazr/custom-cursor';
// Make sure import css also unless its wont work as expected
import '@phazr/custom-cursor/cursor.css';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
`
`tsx
import { useCursor } from '@phazr/custom-cursor';
function InteractiveElements() {
const { setVariant } = useCursor();
return (
onMouseEnter={() => setVariant('text')}
type="text"
onFocus={() => setVariant('input')}
onBlur={() => setVariant('default')}
placeholder="Input field"
/>
onMouseEnter={() => setVariant('sayHi')}
onMouseLeave={() => setVariant('default')}
>
Say Hi Button
$3
`tsx
import { useCursor } from '@phazr/custom-cursor';function CustomCursorExample() {
const { setVariant, setCustomConfig } = useCursor();
const handleCustomCursor = () => {
setCustomConfig({
size: 60,
backgroundColor: '#ff0000',
mixBlendMode: 'normal',
text: 'Click',
textColor: '#ffffff',
fontSize: '14px',
fontFamily: 'Arial, sans-serif',
});
setVariant('custom');
};
return (
onMouseEnter={handleCustomCursor}
onMouseLeave={() => setVariant('default')}
style={{ padding: '20px', backgroundColor: '#f0f0f0' }}
>
Hover for custom cursor with text!
$3
`tsx
import { Cursor } from '@phazr/custom-cursor';
// Make sure import css also unless its wont work as expected
import '@phazr/custom-cursor/cursor.css';
function App() {
return (
Your app content
springConfig={{
damping: 20,
stiffness: 300,
}}
/>
);
}
`$3
`tsx
import { CursorProvider } from '@phazr/custom-cursor';
// Make sure import css also unless its wont work as expected
import '@phazr/custom-cursor/cursor.css';
function App() {
return (
Your app content
);
}
`API Reference
$3
The main provider component that should wrap your application.
`tsx
interface CursorProviderProps {
children: ReactNode;
className?: string;
enableOnTouch?: boolean; // Enable cursor on touch devices (default: false)
}
`$3
The cursor component that renders the actual cursor.
`tsx
interface CursorProps {
className?: string;
springConfig?: {
damping?: number; // Default: 28
stiffness?: number; // Default: 500
};
}
`$3
Hook to control cursor variants and configuration.
`tsx
interface CursorContextType {
variant: CursorVariant;
setVariant: (variant: CursorVariant) => void;
customConfig?: CustomCursorConfig;
setCustomConfig?: (config: CustomCursorConfig) => void;
}const { variant, setVariant, customConfig, setCustomConfig } = useCursor();
`$3
`tsx
type CursorVariant = 'default' | 'link' | 'text' | 'input' | 'sayHi' | 'custom';interface CustomCursorConfig {
size?: number;
backgroundColor?: string;
mixBlendMode?: string;
text?: string;
textColor?: string;
fontSize?: string;
fontFamily?: string;
}
`Built-in Variants
-
default - Standard cursor (16x16px, white, difference blend mode)
- link - Larger cursor for links (64x64px, white, difference blend mode)
- text - Smaller cursor for text (8x8px, white, difference blend mode)
- input - Hidden cursor for inputs (invisible)
- sayHi - Special cursor with "Say Hi" text (90x90px)
- custom - Fully customizable cursor using setCustomConfig()Advanced Usage
$3
`tsx
import { useCursor } from '@phazr/custom-cursor';function Portfolio() {
const { setVariant, setCustomConfig } = useCursor();
const projects = [
{ id: 1, title: 'Project A', action: 'View' },
{ id: 2, title: 'Project B', action: 'Explore' },
{ id: 3, title: 'Project C', action: 'Discover' },
];
const handleProjectHover = (action: string) => {
setCustomConfig({
size: 80,
backgroundColor: '#000000',
text: action,
textColor: '#ffffff',
fontSize: '16px',
mixBlendMode: 'normal',
});
setVariant('custom');
};
return (
{projects.map((project) => (
key={project.id}
onMouseEnter={() => handleProjectHover(project.action)}
onMouseLeave={() => setVariant('default')}
className="project-card"
>
{project.title}
))}
CSS Customization
The package includes default styles that you can override:
`css
/ Override cursor container styles /
.phazr-cursor-container {
/ Your custom styles /
}/ Override cursor text styles /
.phazr-cursor-text {
/ Your custom styles /
}
/ Override "Say Hi" variant styles /
.phazr-cursor-sayhi {
/ Your custom styles /
}
`Troubleshooting
$3
1. Make sure you've wrapped your app with
CursorProvider
2. Ensure you've included the component
3. Check that you're not on a touch device (cursor is hidden by default)$3
If you experience performance issues:
1. Adjust the spring configuration to reduce stiffness
2. Limit the frequency of variant changes
3. Use
React.memo for components that frequently change cursor variants$3
The package automatically handles SSR by:
- Detecting touch devices server-side and client-side
- Hiding cursor on touch devices by default
- Using proper
useEffect hooks for client-side only code$3
By default, the cursor is hidden on touch devices. To enable it:
`tsx
`Browser Support
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. See
CONTRIBUTORS.md` for a list of contributors.MIT © Phazr Inc
---
For more information, visit the GitHub repository.