EmpireUI
Sign inGet Pro
← BlogEnglish · 6 mincustom cursor reactreact cursortailwind css

How to Add a Custom Cursor in React (Free Tailwind Guide)

Learn how to build a custom cursor react component from scratch using Tailwind CSS and hooks, plus free ready-made cursors from Empire UI.

Why Custom Cursors Elevate Your React App

A custom cursor react implementation is one of the fastest ways to make your web application feel premium, polished, and distinctly on-brand. While most developers overlook the cursor entirely, top-tier design studios treat it as a first-class interactive element — a small but constant point of contact between the user and the UI.

Custom cursors reinforce visual identity. Whether you are building a dark glassmorphism portfolio, a bold neobrutalism landing page, or an immersive creative showcase, a cursor that matches your design language adds cohesion that users notice subconsciously. The default OS arrow simply does not fit these aesthetics.

With React and Tailwind CSS, adding a custom cursor requires no heavy library. A handful of hooks, a positioned div, and a few utility classes are all you need. Empire UI also ships 30+ pre-built cursor styles you can drop in with a single import — completely free.

How Custom Cursors Work in the Browser

Browsers expose mouse position through the mousemove event on the window object. By listening to this event, you can track the cursor's clientX and clientY coordinates in real time and sync a custom DOM element to those coordinates using CSS transform: translate(x, y). This is faster than setting `top`/`left` because transform avoids layout recalculation.

To hide the native cursor, you apply cursor: none to the body (or the root element). Your custom element then becomes the visible cursor. The key challenge is making the movement feel instant and smooth — which you solve either by applying the transform directly (zero lag) or adding a subtle transition or spring interpolation for a trailing effect.

Be mindful of accessibility: some users rely on high-contrast OS cursors or assistive pointer tools. Always provide a way for users to opt out, and avoid hiding the cursor entirely on mobile or touch devices where no pointer exists.

Building a Custom Cursor Component from Scratch

Below is a minimal but production-ready custom cursor built with React hooks and Tailwind CSS. It renders a small dot that follows the mouse exactly, plus a larger ring that trails behind with a smooth CSS transition.

import { useEffect, useRef } from 'react';

export function CustomCursor() {
  const dotRef = useRef<HTMLDivElement>(null);
  const ringRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const move = (e: MouseEvent) => {
      const { clientX: x, clientY: y } = e;
      if (dotRef.current) {
        dotRef.current.style.transform = `translate(${x}px, ${y}px)`;
      }
      if (ringRef.current) {
        ringRef.current.style.transform = `translate(${x}px, ${y}px)`;
      }
    };
    window.addEventListener('mousemove', move);
    return () => window.removeEventListener('mousemove', move);
  }, []);

  return (
    <>
      {/* Dot — instant */}
      <div
        ref={dotRef}
        className="pointer-events-none fixed left-0 top-0 z-[9999] h-2 w-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white mix-blend-difference"
      />
      {/* Ring — lagging */}
      <div
        ref={ringRef}
        className="pointer-events-none fixed left-0 top-0 z-[9998] h-8 w-8 -translate-x-1/2 -translate-y-1/2 rounded-full border border-white mix-blend-difference transition-transform duration-150 ease-out"
      />
    </>
  );
}

Mount <CustomCursor /> at the top of your component tree — inside layout.tsx in Next.js, or near the root of your App.tsx in Vite/CRA. Then add cursor-none to your <body> or via a global CSS rule: body { cursor: none; }. The mix-blend-difference Tailwind class makes the cursor invert the colors beneath it, which works beautifully against both light and dark backgrounds without needing separate theme variants.

For a click pulse effect, track mousedown/mouseup events and toggle a scale class: scale-75 on press, scale-100 on release. This one addition makes the cursor feel physically responsive and dramatically improves perceived interactivity.

Adding Hover States and Interactive Zones

A static custom cursor is nice, but a context-aware cursor is exceptional. The pattern is to maintain a cursorVariant state, expose a context or callback, and then apply different Tailwind classes based on the current variant. Common variants include default, hover, text, and drag.

import { createContext, useContext, useState, useEffect, useRef } from 'react';

const CursorContext = createContext({ setVariant: (_: string) => {} });

export function useCursor() {
  return useContext(CursorContext);
}

export function CursorProvider({ children }: { children: React.ReactNode }) {
  const [variant, setVariant] = useState('default');
  const ringRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const move = (e: MouseEvent) => {
      if (ringRef.current) {
        ringRef.current.style.transform =
          `translate(${e.clientX}px, ${e.clientY}px)`;
      }
    };
    window.addEventListener('mousemove', move);
    return () => window.removeEventListener('mousemove', move);
  }, []);

  const sizes: Record<string, string> = {
    default: 'h-8 w-8 border border-white',
    hover: 'h-14 w-14 border-2 border-violet-400 bg-violet-400/10',
    text: 'h-1 w-6 rounded-sm bg-white',
  };

  return (
    <CursorContext.Provider value={{ setVariant }}>
      {children}
      <div
        ref={ringRef}
        className={`pointer-events-none fixed left-0 top-0 z-[9999] -translate-x-1/2 -translate-y-1/2 rounded-full transition-all duration-200 ${sizes[variant] ?? sizes.default}`}
      />
    </CursorContext.Provider>
  );
}

Inside any interactive component, call const { setVariant } = useCursor() and wire it to onMouseEnter/onMouseLeave. Your buttons can expand the ring to hover, your paragraphs can squish it to a text bar, and links can trigger a custom color. This technique requires zero external dependencies and composes naturally with React's component model.

Explore the Empire UI cursors gallery for 30+ production-ready variants — including glassmorphism glow rings, neobrutalism block cursors, spotlight cursors, and magnetic cursors — all downloadable for free and built on this exact pattern.

Performance Tips for Smooth Cursor Animations

The number-one performance mistake with custom cursors is reading `getBoundingClientRect` or `offsetTop` inside the `mousemove` handler. Any DOM read that causes a layout recalculation will throttle your cursor to a janky crawl on lower-end devices. Stick to writing transform values only — reads and writes should never be mixed in the same frame.

Use will-change: transform on the cursor elements (available as will-change-transform in Tailwind v3.3+). This promotes the element to its own compositor layer, allowing the GPU to move it independently of the main thread layout. The result is silky 120fps cursor tracking even while heavy JavaScript is executing elsewhere on the page.

If you want a spring-physics trailing effect instead of a CSS transition, consider a lightweight requestAnimationFrame loop that lerps the cursor position toward the target each frame: current += (target - current) * 0.15. This single-line interpolation creates an organic, magnetic feel without importing any animation library. See Empire UI animated components for more patterns like this applied to floating navbars and moving borders.

Using Empire UI's Pre-Built Cursor Library

Building a cursor from scratch is a great learning exercise, but if you want production-grade cursors with zero setup, Empire UI's cursor component library has you covered. Every cursor is a self-contained React component, styled with Tailwind, and ships with TypeScript types. Choose from styles like Glassmorphism Glow, Neobrutalism Block, Claymorphism Blob, Retro Pixel, and more.

Installation is straightforward — copy the component file into your project (no npm package needed, no lock-in), add cursor-none to your body, and drop the component into your layout. Each cursor also supports variant props so you can pass hoverVariant and clickVariant without wiring up context yourself.

For teams building with a consistent design system, explore Empire UI Templates to find full industry-specific starter kits that already include matching cursors, scrollbars, backgrounds, and components — all under the same free license. You can also use the Empire UI MCP server to generate custom cursor components directly from a natural-language prompt inside your AI coding assistant.

FAQ

How do I hide the default browser cursor in React?

Add cursor: none to your body element via a global CSS file or a Tailwind [&>body]:cursor-none directive in your root layout. Make sure your custom cursor element is rendered at the root level so it is always visible regardless of which page the user is on.

Will a custom cursor work on mobile and touch devices?

Touch devices do not emit mousemove events, so your custom cursor simply will not appear — which is the correct behavior. You can add a check with window.matchMedia('(pointer: fine)') to conditionally render the cursor only on pointer devices and avoid any unnecessary event listeners on mobile.

How do I make the cursor change on hover over a button?

Use a React context or a simple state variable to track a cursorVariant. On your button's onMouseEnter, set the variant to 'hover', and on onMouseLeave reset it to 'default'. Apply different Tailwind size or color classes to the cursor element based on the current variant.

Does a custom cursor affect accessibility?

Hiding the native cursor can create issues for users who depend on high-contrast system cursors or accessibility tools. Always test with keyboard-only navigation and consider respecting the prefers-reduced-motion media query to disable animated cursor effects for users who opt out of motion.

Can I use Empire UI cursors in a commercial project?

Yes — all Empire UI components including the cursor library are free for personal and commercial use. There is no attribution requirement, though a star on the repo is always appreciated. Visit the cursors page to browse and copy the components directly.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

How to Build an Animated Button in React + Tailwind (Free)How to Build a Floating Navbar in React + Tailwind (2026 Guide)How to Create a Moving Border Effect in React + TailwindHow to Add a Spotlight Effect in React + Tailwind (Free)