@holmesdev/cursors
v1.0.3
Published
React cursor component — fully customizable, accessibility-first, and SVG-based
Downloads
120
Readme
🖱️ @holmesdev/cursors
Fully customizable mouse cursor component for React — SVG-based and accessibility-first.
📚 Contents
- Overview
- Getting Started
- Configuration
- Cursor Effects
- Custom SVGs
- Examples
- Accessibility
- About
- Contributing
- License
📖 Overview
@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.
Features
- 🎨 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
🚀 Getting Started
Installation
npm install @holmesdev/cursors
# or
yarn add @holmesdev/cursorsUsage
import ReactCursor from "@holmesdev/cursors";
function App() {
return (
<>
{/* Import component into your top-level component */}
<ReactCursor />
<h1>Hover around the page!</h1>
</>
);
}
export default App;🛠️ Configuration
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
Global options are passed directly to the component through props, and apply to the entire component. For example:
<ReactCursor enable={false} />| 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. |
Layer Options
React Cursors have one or more layers with their own set of options. For example:
<ReactCursor
layers={[
{
// Layer options here... e.g.
SVG: "circle",
},
]}
/>| Prop | Type | Default | Description |
| ---------------------- | -------------------------------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SVG? | SvgDefaultStyles | FC<SVGProps<SVGSVGElement>> | "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
Cursor effects allow you to dynamically change the cursor's appearance when users interact with certain elements. Effects are applied per-layer.
Effect Types
hover- Triggered when the mouse hovers over interactive elements (defined byhoverSelector)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.*
Effect Properties
Each effect (hover or click) accepts the following properties:
| Prop | Type | Description |
| ------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SVG? | SvgDefaultStyles | FC<SVGProps<SVGSVGElement>> | 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"). |
Customizing Hover Targets
By default, hover effects trigger on interactive elements: "a, button, [role='button'], input, textarea, select". You can customize this with the hoverSelector prop:
<ReactCursor
hoverSelector=".custom-hover, [data-interactive]" // Custom CSS selector
/>🖼️ Custom SVGs
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.
- Copy/paste your
.svgcode into the input section. - Copy/paste the output into a new JSX file (e.g.
MyCustomSVG.jsx). - Import the SVG Component within your application.
- Copy/paste your
METHOD TWO: Alternatively, you can automonize this conversion within your application by using React SVGR within your project. For instance, with Vite:
- Install the dependency.
npm install --save-dev vite-plugin-svgr- Configure the plugin.
// vite.config.js plugins: [ svgr() ],- Import the SVG normally.
Importing Custom SVGs
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 (
<ReactCursor layers={[
{
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
.svgfile:
- Height and Width: Omit these in the SVG. The cursor component’s
sizeprop will control the dimensions.- Colors: Use
fill="currentColor"andstroke="currentStroke"for dynamic colors. This allows the cursor component’sfillandstrokeprops to control the appearance.
- ⚠️ If you hard-code colors like
fill="red", the SVG will always display in red, ignoring the component’s props.
📝 Examples
- 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.
🧠 Accessibility
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.
ℹ️ About Haus of Cards
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.
🤝 Contributing
Refer to CONTRIBUTING for guidelines. Please also review our CODE OF CONDUCT.
🧾 License
MIT © 2025 (refer to LICENSE).
