React cursor component — fully customizable, accessibility-first, and SVG-based
npm install @holmesdev/cursors



!TypeScript
Fully customizable mouse cursor component for React — SVG-based and accessibility-first.
- Overview
- Getting Started
- Configuration
- Global Options
- Layer Options
- Cursor Effects
- Custom SVGs
- Examples
- Accessibility
- About
- Contributing
- License
_@holmesdev/cursors_ lets you replace the default pointer with a beautiful, customizable, multi-layer cursor system for React — complete with prebuilt shapes, deep configuration options, and built-in accessibility.
- 🎨 Multi-layer support — stack any number of customizable layers to build complex cursor designs
- 🧩 Built-in shapes — arrow, circle, crosshair, and square
- 📝 Custom shapes — use any user-provided SVG of your choice as a cursor layer
- ⚙️ Deep configuration — fine-grained control over size, color, behaviour, and more
- ♿ Accessible by default — fully baked-in under the hood, including automatic safety fallbacks
- 🔧 Fully declarative React API
- 💻 TypeScript support — fully typed props and layers
- 🪶 Lightweight & performant — optimized rendering with minimal overhead
- 📦 Small bundle footprint — ideal for any modern React application
- 🔓 Open-source (MIT) — freely available and fully open-source
``bash`
npm install @holmesdev/cursorsor
yarn add @holmesdev/cursors
`jsx
import ReactCursor from "@holmesdev/cursors";
function App() {
return (
<>
{/ Import component into your top-level component /}
export default App;
`
Options for a React Cursor are divided into 2 types:
- _global_ - applies to the entire cursor component, or
- _layer_ - applies to each individual layer separately.
Global options are passed directly to the component through props, and apply to the entire component. For example:
`jsx`
| Prop | Type | Default | Description |
| ---------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| enable? | boolean | true | Enables or disables the cursor entirely. |showSystemCursor?
| | boolean | true | Shows or hides the system cursor. |layers?
| | CursorLayer[] _(refer to Layer Options)_ | [{ fill: "black", stroke: "white", strokeSize: 10, size: { width: 20, height: 20 } }] | Defines each cursor layer. |mixBlendMode?
| | CSSProperties["mixBlendMode"] | "normal" | Controls how the cursor blends with the background. |zIndex?
| | number | 2147483647 | Cursor z-index. Default is max value. |ignoreAccessibility?
| | boolean | false | Bypass system accessibility detection. By default, if a user has any accessibility flags set in their browser, the custom cursor will disable and the system cursor will show instead. |hoverSelector?
| | string | "a, button, [role='button'], input, textarea, select" | CSS selector that triggers hover effects. |
React Cursors have one or more layers with their own set of options. For example:
`jsx`
{
// Layer options here... e.g.
SVG: "circle",
},
]}
/>
| Prop | Type | Default | Description |
| ---------------------- | -------------------------------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SVG? | SvgDefaultStyles \| FC | "circle" | Defines which SVG image to use for the cursor layer. SvgDefaultStyles include: "circle", "cross", "square", "arrow". To supply your own SVG, you must first convert it to a React Functional Component. See more here |fill?
| | string | "black" | Replaces the fill=currentColor within layer's SVG file. Can supply any valid CSS color, such as "black", "#000", "rgb(0,0,0)", etc. |stroke?
| | string | "white" | Replaces the stroke=currentStroke within layer's SVG file. |strokeSize?
| | number | 10 | Adjusts the stroke size of the layer's SVG. |opacity?
| | number | 1 | Adjust the opacity of the layer. |size?
| | { height: number, width: number } | { height: 25, width: 25 } | Adjust the px height/width of the SVG. Aspect-ratio is retained by default, so the smallest value will usually be used for the size. |preserveAspectRatio?
| | boolean | true | Preserves the aspect-ratio of the SVG layer, regardless of the size attribute provided. If false, the SVG will stretch/scale exactly as specified by the size attribute. |delay?
| | number | 0 | The amount of "lag" that the layer will have, with respect to the actual system cursor position. Higher number = greater lag. |effects?
| | CursorEffects _(refer to Cursor Effects)_ | undefined | Defines hover and click effect states. |hotspot?
| | { x: number, y: number } | { x: width/2, y: height/2 } | X/Y coordinate override for where the actual "click" hotspot of the cursor layer is. By default, the click is centered within the SVG image. If top-left is required, for example when using an "arrow" style SVG, the hotspot should be set at x=0 & y=0. |
Cursor effects allow you to dynamically change the cursor's appearance when users interact with certain elements. Effects are applied per-layer.
- hover - Triggered when the mouse hovers over interactive elements (defined by hoverSelector)click
- - Triggered when the user presses down the mouse button (released on mouse up)
\Note: click effects take precedence over hover effects when both are active.\
Each effect (hover or click) accepts the following properties:
| Prop | Type | Description |
| ------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SVG? | SvgDefaultStyles \| FC | Changes the SVG shape during the effect. Can use built-in shapes ("circle", "cross", "square", "arrow") or custom SVG component. |fill?
| | string | Changes the fill color during the effect. |stroke?
| | string | Changes the stroke color during the effect. |strokeSize?
| | number | Changes the stroke size during the effect. |opacity?
| | number | Changes the opacity (0-1) during the effect. |scale?
| | number | Scales the cursor size (1 = normal, 2 = double size, 0.5 = half size). Multiplies the layer's base size. |hotspot?
| | { x: number, y: number } | Overrides the X/Y coordinates for the SVGs click location. This is useful when changing to an SVG that uses a different click point (e.g. changing from "circle" to "arrow"). |
By default, hover effects trigger on interactive elements: "a, button, [role='button'], input, textarea, select". You can customize this with the hoverSelector prop:
`jsx`
/>
The cursor component comes with preset SVGs like "circle", "cross", "square", and "arrow".
If you want to use a custom SVG, that is also supported. However, the ReactCursor component does not accept raw SVG files/strings directly. Instead, the SVG must first be converted into a React Functional Component. These are the recommended methods:
- METHOD ONE: The easiest method is to copy/paste your SVG code directly into the online SVGR playground. This will output a JSX component that you can use directly.
1. Copy/paste your .svg code into the input section.MyCustomSVG.jsx
2. Copy/paste the output into a new JSX file (e.g. ).
3. Import the SVG Component within your application.
- METHOD TWO: Alternatively, you can automonize this conversion within your application by using React SVGR within your project. For instance, with Vite:
1. Install the dependency.
`bash`
npm install --save-dev vite-plugin-svgr
2. Configure the plugin.
`js`
// vite.config.js
plugins: [ svgr() ],
3. Import the SVG normally.
#### Importing Custom SVGs
`jsx
import MyCustomSVG from "/path/to/file.jsx"; // for manually created SVG component (i.e. METHOD ONE)
import MyCustomSVG from "/path/to/file.svg"; // for react-svgr (i.e. METHOD TWO)
export default function Example() {
return (
SVG: MyCustomSVG, // can now fully-customize your SVG through the cursor component
fill: "red",
stroke: "blue",
}
]}>
);
}
`
> NOTE
>
> To make your SVG fully customizable, avoid hard-coding values. Hard-coded values will always take precedence over the cursor component’s props.
>
> For example, within your actual .svg file:size
>
> - Height and Width: Omit these in the SVG. The cursor component’s prop will control the dimensions.fill="currentColor"
> - Colors: Use and stroke="currentStroke" for dynamic colors. This allows the cursor component’s fill and stroke props to control the appearance.fill="red"
> - ⚠️ If you hard-code colors like , the SVG will always display in red, ignoring the component’s props.
- Multiple examples have been pre-made and can found here
- These example can be viewed in realtime by accessing the examples section of our website
- Alternatively, you can demo the cursor configuration by accessing our live demo testbed.
By default, the component respects user system preferences and disables itself when:
- Reduced motion is requested
- High contrast or forced colors mode is active
- Coarse pointer (touch) is detected
You can override this by setting ignoreAccessibility={true}`.
Refer to ACCESSIBILITY for more details.
Haus of Cards is a student team who created the React Cursor project at Holmesglen Institute of TAFE in 2025 as part of their course.
Refer to CONTRIBUTING for guidelines. Please also review our CODE OF CONDUCT.
MIT © 2025 (refer to LICENSE).