Examples/Custom Pointer

Custom Pointer

A themed pointer scoped to one region, plus a three-item menu.

Move. Then right-click anything here.

Live customization

Preset Themes

Pointer Icon

Colors

Pointer Color

Sizes

What's different: the pointer only exists inside the PointerProvider's region, and the menu is this page's own three items. Change a control and both the pointer and the source below update. The right-click circle animation is not shown inside an anyclick region (the pointer package skips it once the event is handled).

app/providers.tsx
'use client';
import { PointerProvider } from '@ewjdev/anyclick-pointer';
import { AnyclickProvider } from '@ewjdev/anyclick-react';
import { createHttpAdapter } from '@ewjdev/anyclick-github';
import { Default } from 'lucide-react';
const adapter = createHttpAdapter({ endpoint: '/api/feedback' });
const theme = {
colors: {
pointerColor: '#3b82f6',
circleColor: 'rgba(59, 130, 246, 0.4)',
circleBorderColor: '#3b82f699',
},
sizes: { pointerSize: 24, circleSize: 44, circleBorderWidth: 2 },
pointerIcon: (
<Default
size={24}
strokeWidth={2}
fill="rgba(59, 130, 246, 0.3)"
stroke="#3b82f6"
/>
),
};
const menuItems = [
{ type: 'issue', label: 'Report pointer glitch', showComment: true },
{ type: 'copy-theme', label: 'Copy pointer theme',
onClick: () => navigator.clipboard.writeText(JSON.stringify(theme)) },
{ type: 'feature', label: 'Send feedback', showComment: true },
];
export function Providers({ children }) {
return (
<PointerProvider theme={theme}>
<AnyclickProvider adapter={adapter} scoped menuItems={menuItems}>
{children}
</AnyclickProvider>
</PointerProvider>
);
}
// This page also calls usePointer().setTheme(theme) when you change a control.
Implementation details, API and theme options

What You Get

  • Custom themed cursor that follows mouse movement
  • Smooth right-click animation with expanding circle
  • Press effect when clicking (scaled pointer + faint circle)
  • Menu-aware: shows pointer over menus, circle elsewhere when menu open
  • High z-index to appear above all UI elements
  • Respects prefers-reduced-motion for accessibility
  • Direct DOM manipulation for 60fps performance
  • Dynamic theme updates via usePointer hook

Implementation

1. Install the Package

npm install @ewjdev/anyclick-pointer
# or
yarn add @ewjdev/anyclick-pointer

2. Basic Setup with PointerProvider

app/providers.tsx
'use client';
import { PointerProvider } from '@ewjdev/anyclick-pointer';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<PointerProvider>
{children}
</PointerProvider>
);
}

3. Wrap Your App

app/layout.tsx
import { Providers } from './providers';
export default function RootLayout({
children
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}

Custom Theme

Customize colors, sizes, and even replace the pointer icon. Use fill="none" for transparent background or a semi-transparent color for filled:

app/providers.tsx
import { PointerProvider } from '@ewjdev/anyclick-pointer';
import { Default } from 'lucide-react';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<PointerProvider
theme={{
colors: {
pointerColor: '#3b82f6',
circleColor: 'rgba(59, 130, 246, 0.4)',
circleBorderColor: '#3b82f699',
},
sizes: {
pointerSize: 24,
circleSize: 44,
circleBorderWidth: 2,
},
// fill="none" for transparent, or use rgba for semi-transparent fill
pointerIcon: <Default
size={24}
fill="rgba(59, 130, 246, 0.15)"
stroke="#3b82f6"
/>,
}}
config={{
visibility: 'always',
hideDefaultCursor: true,
zIndex: 10001,
}}
>
{children}
</PointerProvider>
);
}

Dynamic Theme Updates

Update the pointer theme at runtime using the usePointer hook:

import { usePointer } from '@ewjdev/anyclick-pointer';
import { Crosshair } from 'lucide-react';
function ThemeSwitcher() {
const { setTheme, setInteractionState } = usePointer();
const applyDarkTheme = () => {
setTheme({
colors: {
pointerColor: '#8b5cf6',
circleColor: 'rgba(139, 92, 246, 0.4)',
},
sizes: {
pointerSize: 28,
circleSize: 50,
},
pointerIcon: <Crosshair size={28} fill="white" stroke="#8b5cf6" />,
});
};
const triggerCircle = () => {
setInteractionState('rightClick');
};
return (
<div>
<button onClick={applyDarkTheme}>Apply Dark Theme</button>
<button onClick={triggerCircle}>Show Circle</button>
</div>
);
}

Integration with AnyclickProvider

Combine with the feedback system for a complete experience:

app/providers.tsx
'use client';
import { AnyclickProvider } from '@ewjdev/anyclick-react';
import { PointerProvider } from '@ewjdev/anyclick-pointer';
import { createHttpAdapter } from '@ewjdev/anyclick-github';
const adapter = createHttpAdapter({ endpoint: '/api/feedback' });
export function Providers({ children }: { children: React.ReactNode }) {
return (
<AnyclickProvider adapter={adapter}>
<PointerProvider
theme={{
colors: {
pointerColor: '#3b82f6',
circleColor: 'rgba(59, 130, 246, 0.4)',
},
}}
config={{
visibility: 'always',
hideDefaultCursor: true,
}}
>
{children}
</PointerProvider>
</AnyclickProvider>
);
}

Configuration Reference

Theme Options

PropertyDefaultDescription
colors.pointerColorcurrentColorPointer icon color
colors.circleColorrgba(59, 130, 246, 0.4)Circle background
sizes.pointerSize24Pointer icon size (px)
sizes.circleSize44Circle size (px)
pointerIconMousePointer2Custom icon component

Hook Methods

MethodDescription
setTheme(theme)Update theme colors, sizes, and icon
setConfig(config)Update behavior config
setEnabled(bool)Enable/disable the pointer
setInteractionState(state)Trigger states: 'normal' | 'rightClick' | 'pressing'

Your changes are live!

The customizations you made above are applied to the actual pointer on this page. Navigate to other pages to see your theme persist, or refresh to reset.

Basic Setup Example