# react-smart-mouse

> Smart custom cursor for React & Next.js — a cursor follower that adapts its shape, size and color to the element it hovers. Zero dependencies, TypeScript, App Router ready.

Latest version **2.2.0** (published 2026-07-15) · MIT license · 0 weekly downloads

## Install

```sh
npm install react-smart-mouse
pnpm add react-smart-mouse
yarn add react-smart-mouse
bun add react-smart-mouse
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 2.2.0 |
| Published | 2026-07-15 |
| First published | 2023-07-16 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=18 |
| Dependencies | 0 |
| Unpacked size | 120.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 0 |
| Author | Vadym Bilan |
| Maintainers | bilvad |
| Keywords | react, nextjs, next, cursor, custom-cursor, cursor-follower, mouse, mouse-follower, mouse-tracker, pointer, hover, hover-effect, animation, ui, typescript, component, smart |

## Links

- npm: https://www.npmjs.com/package/react-smart-mouse
- Repository: https://github.com/BilVaD1/SmartMouse
- Homepage: https://github.com/BilVaD1/SmartMouse#readme
- Issues: https://github.com/BilVaD1/SmartMouse/issues
- Funding: https://www.patreon.com/VadymDEV
- npm.io page: https://npm.io/package/react-smart-mouse

## Alternatives

- [@progress/kendo-ooxml](https://npm.io/package/@progress/kendo-ooxml.md) — 152.1K weekly downloads
- [@progress/kendo-react-ripple](https://npm.io/package/@progress/kendo-react-ripple.md) — 8.0K weekly downloads
- [@progress/kendo-react-orgchart](https://npm.io/package/@progress/kendo-react-orgchart.md) — 4.3K weekly downloads
- [@praxisui/dynamic-fields](https://npm.io/package/@praxisui/dynamic-fields.md) — 2.4K weekly downloads
- [@mesalvo/react-ui](https://npm.io/package/@mesalvo/react-ui.md) — 1.7K weekly downloads

## Recent versions

- 2.2.0 (latest) — 2026-07-15
- 2.1.0 — 2026-07-15
- 2.0.3 — 2025-02-02
- 2.0.2 — 2025-02-02
- 2.0.1 — 2025-02-02
- 2.0.0 — 2025-02-02
- 1.0.7 — 2025-02-01
- 1.0.6 — 2023-07-16
- 1.0.5 — 2023-07-16
- 1.0.4 — 2023-07-16
- 1.0.3 — 2023-07-16
- 1.0.2 — 2023-07-16
- 1.0.1 — 2023-07-16
- 1.0.0 — 2023-07-16

## README

# react-smart-mouse

> Smart custom cursor for React & Next.js — a cursor follower that automatically adapts its shape, size and color to whatever it hovers.

[![npm version](https://img.shields.io/npm/v/react-smart-mouse.svg)](https://www.npmjs.com/package/react-smart-mouse)
[![npm downloads](https://img.shields.io/npm/dm/react-smart-mouse.svg)](https://www.npmjs.com/package/react-smart-mouse)
[![CI](https://github.com/BilVaD1/SmartMouse/actions/workflows/ci.yml/badge.svg)](https://github.com/BilVaD1/SmartMouse/actions/workflows/ci.yml)
[![bundle size](https://img.shields.io/bundlephobia/minzip/react-smart-mouse)](https://bundlephobia.com/package/react-smart-mouse)
[![license](https://img.shields.io/npm/l/react-smart-mouse.svg)](https://github.com/BilVaD1/SmartMouse/blob/main/LICENSE)

Unlike a plain cursor follower, `SmartMouse` knows **which element it's hovering** — links, buttons, images, inputs, text — and morphs into a different style for each one. Drop it in once and your whole app gets an interactive, animated cursor.

![](https://github.com/BilVaD1/SmartMouse/blob/main/HoverDemo.gif)

[Live Demo](https://myportfoliobilvad.netlify.app/SmartMouse)

## Features

- 🎯 **Element-aware** — automatically restyles itself over `a`, `button`, `img`, `input`, `textarea`, checkboxes, radios, and more
- ⚛️ **React 16.14 – 19** and **Next.js (Pages & App Router)** — ships with `"use client"`, import it anywhere
- 🚀 **Fast** — position updates bypass React re-renders; a single injected style rule hides the native cursor
- 🧲 **Magnetic hover** — the follower snaps to the center of elements you mark as magnetic
- 🌊 **Smooth trailing** — optional lerp smoothing for that lagging-cursor feel
- 🏷️ **Text labels** — show "View", "Drag", etc. inside the cursor via a `mouselabel` attribute
- 🧩 **Custom cursor content** — pass `children` to replace the dot with anything (emoji, rings, SVG)
- 🧊 **Glass mode** — frosted-glass cursor with blurred backdrop and translucent color tints
- 🔍 **Image magnifier** — the cursor becomes a zoom lens over images marked with `mousemagnify`
- ✨ **Check animation** — the cursor pulses when a checkbox or radio is toggled
- 📱 **Touch-aware** — automatically disables itself on touch-only devices
- ♿ **Accessible** — respects `prefers-reduced-motion` out of the box
- 🖱️ **Click feedback** — optional press animation via `clickScale`
- 🎨 **Fully customizable** — per-tag styles, per-class styles, any-CSS-selector styles, per-element `mousecustom` overrides, blend modes
- 🪶 **Zero dependencies**, ~2 kB min+gzip, TypeScript types included

## Installation

```bash
npm install react-smart-mouse
```

## Quick start

```tsx
import SmartMouse from 'react-smart-mouse'

const App = () => (
  <div>
    {/* Your app */}
    <SmartMouse />
  </div>
)
```

That's it — the native cursor is replaced by an animated follower that reacts to every element it passes over.

### Customized

```tsx
<SmartMouse
  // per-tag styles
  pStyle={{ color: 'rgba(191, 75, 30, 1)', height: '40px', width: '40px' }}
  aStyle={{ color: 'rgba(132, 245, 39, 0.8)', height: '20px', width: '20px' }}
  buttonStyle={{ color: 'rgba(118, 39, 245, 0.8)', height: '20px', width: '20px' }}
  // per-class styles
  classStyle={{
    'some-class': { width: '50px', height: '50px', color: '#996099' },
  }}
  // defaults for everything else
  defaultWidth="35px"
  defaultHeight="35px"
  defaultColor="rgba(0, 0, 0, 0.5)"
  // new in 2.1
  clickScale={0.8}
  blendMode="difference"
  transitionDuration="0.5s"
  lerp={0.15}
  magneticSelector=".btn, .nav-link"
/>
```

Colors accept HEX, RGB(A), or named colors (`'red'`).

## Recipes

### Smooth trailing cursor

`lerp` controls how quickly the follower catches up with the real cursor — `1` sticks to it, lower values trail behind:

```tsx
<SmartMouse lerp={0.15} />
```

### Magnetic elements

While hovered, magnetic elements pull the follower to their center. Mark them with an attribute or a selector (pairs beautifully with `lerp`):

```tsx
<SmartMouse magneticSelector=".magnetic" lerp={0.2} />

<button className="magnetic">Hover me</button>
{/* or per element, no prop needed: */}
<button mousemagnetic="">Hover me</button>
```

### Text labels ("View", "Drag", …)

Show a label inside the cursor while hovering an element — the agency-site classic:

```tsx
<SmartMouse textStyle={{ fontSize: '14px', fontWeight: 600 }} />

<div className="project-card" mouselabel="View"
     mousecustom={JSON.stringify({ width: '70px', height: '70px' })}>
  ...
</div>
```

### Fully custom cursor

Pass `children` to replace the built-in dot with anything — the follower still tracks, hides, and click-scales for you:

```tsx
<SmartMouse clickScale={0.8}>
  <div style={{ width: 24, height: 24, border: '2px solid black', borderRadius: '50%' }} />
</SmartMouse>
```

### Glass cursor

One prop turns the cursor into frosted glass — the backdrop blurs underneath it and all element colors render as translucent tints:

```tsx
<SmartMouse glass />
```

### Magnify images

Mark any image and the cursor becomes a circular zoom lens while hovering it:

```tsx
<SmartMouse magnifyZoom={2.5} magnifySize={140} />

<img src="/photo.jpg" data-mousemagnify="" alt="Zoom me" />
```

### Checkbox pulse

Enabled by default — toggling any checkbox or radio makes the cursor pulse. Turn it off with:

```tsx
<SmartMouse checkAnimation={false} />
```

### Style any element by CSS selector

`selectorStyle` matches the hovered element (or any ancestor) against arbitrary CSS selectors — a superset of `classStyle`:

```tsx
<SmartMouse
  selectorStyle={{
    'nav a':          { width: '20px', height: '20px', color: '#0af' },
    '[data-magnify]': { width: '80px', height: '80px', color: 'rgba(0,0,0,0.2)' },
  }}
/>
```

> **TypeScript note:** custom JSX attributes like `mouselabel` may upset strict JSX typing. Every attribute also works with a `data-` prefix: `data-mouselabel`, `data-mousecustom`, `data-mousemagnetic`, `data-mousemagnify`.

## Next.js

`SmartMouse` ships with the `"use client"` directive built into the bundle, so with the **App Router** you can import it directly in a Server Component such as `app/layout.tsx` — no wrapper component needed:

```tsx
// app/layout.tsx
import SmartMouse from 'react-smart-mouse'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <SmartMouse />
      </body>
    </html>
  )
}
```

Works with the **Pages Router** too — just render it in `_app.tsx`. Tested with Next.js 13 → 16.

## Props

| prop                 | type        | default                                                                                             | description                                                                                        |
| -------------------- | ----------- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| pStyle               | object      | `{ color: 'rgba(255, 0, 0, 0.5)', height: '35px', width: '35px', top: '-5px', left: '-10px' }`      | Style over `p` elements.                                                                |
| aStyle               | object      | `{ color: 'rgba(0, 128, 0, 1)', height: '15px', width: '15px', top: '-5px', left: '-10px' }`        | Style over `a` elements.                                                                |
| buttonStyle          | object      | `{ color: 'rgba(248, 246, 15, 1)', height: '15px', width: '15px', top: '-5px', left: '-10px' }`     | Style over `button` elements.                                                           |
| spanStyle            | object      | `{ color: 'rgba(255, 165, 0, 0.5)', height: '35px', width: '35px', top: '-5px', left: '-10px' }`    | Style over `span` elements.                                                             |
| imgStyle             | object      | `{ color: 'rgba(248, 246, 15, 0.7)', height: '15px', width: '15px', top: '-5px', left: '-10px' }`   | Style over `img` elements.                                                              |
| inputStyle           | object      | `{ color: 'rgba(67, 39, 245, 1)', height: '30px', width: '2px', top: '-15px', left: '0px' }`        | Style over `input` elements (text caret look).                                          |
| textareaStyle        | object      | `{ color: 'rgba(227, 104, 133, 1)', height: '30px', width: '3px', top: '-15px', left: '0px' }`      | Style over `textarea` elements.                                                         |
| labelStyle           | object      | `{ color: 'rgba(128, 128, 0, 0.5)', height: '35px', width: '35px', top: '-5px', left: '-10px' }`    | Style over `label` elements.                                                            |
| liStyle              | object      | `{ color: 'rgba(204, 147, 198, 0.5)', height: '35px', width: '35px', top: '-5px', left: '-10px' }`  | Style over `li` elements.                                                               |
| checkboxStyle        | object      | `{ color: 'rgba(129, 242, 63, 1)', height: '10px', width: '10px', top: '-5px', left: '-5px' }`      | Style over checkboxes.                                                                  |
| radioButtonStyle     | object      | `{ color: 'rgba(150, 63, 242, 1)', height: '10px', width: '10px', top: '-5px', left: '-5px' }`      | Style over radio buttons.                                                               |
| rangeStyle           | object      | `{ color: 'rgba(67, 39, 245, 0.8)', height: '18px', width: '18px', top: '-9px', left: '-9px' }`     | Style over `range` sliders. **New in 2.2**                                              |
| classStyle           | object      | none                                                                                                | Styles keyed by className: `{ [className]: ElementStyle }`.                             |
| selectorStyle        | object      | none                                                                                                | Styles keyed by any CSS selector, matched with `closest()`. **New in 2.1**              |
| defaultWidth         | string      | `35px`                                                                                              | Default follower width.                                                                 |
| defaultHeight        | string      | `35px`                                                                                              | Default follower height.                                                                |
| defaultTop           | number \| string | `-5`                                                                                           | Default top offset of the follower.                                                     |
| defaultLeft          | number \| string | `-10`                                                                                          | Default left offset of the follower.                                                    |
| defaultColor         | string      | `rgba(0, 0, 0, 0.5)`                                                                                | Default follower color.                                                                 |
| zIndex               | number      | `3000`                                                                                              | z-index of the follower. **New in 2.1**                                                 |
| transitionDuration   | string      | `0.7s`                                                                                              | CSS duration for size/color morphs. **New in 2.1**                                      |
| clickScale           | number      | `1`                                                                                                 | Scale applied while the mouse button is pressed, e.g. `0.8`. **New in 2.1**             |
| blendMode            | string      | none                                                                                                | CSS `mix-blend-mode` for the follower, e.g. `'difference'`. **New in 2.1**              |
| hideOnLeave          | boolean     | `true`                                                                                              | Hide the follower when the cursor leaves the window. **New in 2.1**                     |
| lerp                 | number      | `1`                                                                                                 | Smoothing factor in (0, 1]. Lower = more trailing lag, e.g. `0.15`. **New in 2.1**      |
| magneticSelector     | string      | none                                                                                                | CSS selector for magnetic elements — follower snaps to their center. **New in 2.1**     |
| textStyle            | object      | `{ fontSize: '12px', color: '#fff' }`                                                              | Style for `mouselabel` text (any CSSProperties). **New in 2.1**                         |
| respectReducedMotion | boolean     | `true`                                                                                              | Disable animations when the OS requests reduced motion. **New in 2.1**                  |
| checkAnimation       | boolean     | `true`                                                                                              | Pulse the cursor when a checkbox/radio is toggled. **New in 2.2**                       |
| glass                | boolean     | `false`                                                                                             | Frosted-glass cursor: blurred backdrop, translucent colors. **New in 2.2**              |
| magnifyZoom          | number      | `2`                                                                                                 | Zoom factor for `mousemagnify` images. **New in 2.2**                                   |
| magnifySize          | number      | `120`                                                                                               | Lens diameter (px) for the image magnifier. **New in 2.2**                              |
| children             | ReactNode   | none                                                                                                | Custom cursor content rendered instead of the built-in dot. **New in 2.1**              |
| debug                | boolean     | `false`                                                                                             | Keep the native cursor visible and log hovered elements to the console.                 |

```ts
interface ElementStyle {
  width?: string
  height?: string
  color?: string
  top?: number | string
  left?: number | string
}
```

## Style priority

When several rules could apply to the hovered element, the follower resolves them in this order:

1. `mousecustom` attribute on the element (or an ancestor)
2. `selectorStyle` match on the element or an ancestor
3. `classStyle` match on the element's className
4. Per-tag style (`buttonStyle`, `aStyle`, …)
5. Defaults (`defaultWidth`, `defaultColor`, …)

## Per-element overrides with `mousecustom`

Give any single element its own cursor style with the `mousecustom` attribute (also matches when hovering the element's children, and works as `data-mousecustom`):

```tsx
<input type="text" mousecustom={JSON.stringify({ height: '50px', color: '#e1e89e' })} />
```

## Touch devices & accessibility

On devices without a fine pointer (phones, tablets) the component renders nothing and leaves the native behavior untouched — no configuration needed.

When the OS reports `prefers-reduced-motion: reduce`, all transitions and trailing are disabled while the cursor stays fully functional. Opt out with `respectReducedMotion={false}`.

## TypeScript

Full types are shipped with the package:

```ts
import SmartMouse, { SmartMouseProps, ElementStyle } from 'react-smart-mouse'
```

## License

MIT © [Vadym Bilan](https://github.com/BilVaD1). See [LICENSE](https://github.com/BilVaD1/SmartMouse/blob/main/LICENSE).

---
_Source: https://npm.io/package/react-smart-mouse · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
