How to use the CSS cursor property

The CSS cursor property carries a lot of weight for user experience. It’s like a quiet guide that instantly tells users what can be done on your page — where they can input text, which elements are clickable, draggable, or resizable.

How To Use The CSS Cursor Property

Getting these visual clues right doesn’t just make your site look more polished; it makes it feel more interactive and intuitive. When users don’t have to guess how to interact with your interface, they spend less time confused and more time focused on what really matters.

In this tutorial, we’ll explore the syntax of the cursor property, its supported values, and how to use them effectively in real-world examples.

What is the CSS cursor property?

The CSS cursorproperty allows developers to control the mouse pointer’s appearance when users hover over specific elements. It’s a subtle but powerful tool that helps communicate functionality before users even click, wait, text, or drag anything.

Here’s what the basic CSS cursor property syntax looks like:

cursor: pointer;

This basic form uses a predefined keyword value (pointer) that tells the browser which built-in cursor style to use.

For a full list of cursor values (including both popular and lesser-known ones), check out this CodePen:

See the Pen
Cursor Values Preview
by Emmanuel Odioko (@Emmanuel-Odioko)
on CodePen.

Input modality and cursor considerations

Different devices handle cursors differently, so it’s important to plan for various input methods:

In touch devices like your phones and tablets, the cursors are not visible. This means the cursor: pointer won’t help mobile users identify clickable elements. And so, it is important to pair cursor styles with other visual indicators like button styling that make it obvious this button is clickable:

.button {
/* Clear visual styling for mobile */
background-color: #4f46e5;
color: white;
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;

/* Mobile-friendly touch targets */
min-height: 44px;
min-width: 44px;

/* Visual feedback without cursor */
transition: background-color 0.2s;
}

.button:active {
background-color: #3730a3; /* Darker on tap */
transform: scale(0.98); /* Slight press effect */
}

.button:disabled {
background-color: #9ca3af;
color: #6b7280;
}

/* Only add cursor on devices that support hover */
@media (hover: hover) {
.button {
cursor: pointer;
}

.button:hover {
background-color: #4338ca;
}
}

Hybrid devices like laptops with touchscreens, surface tablets with keyboards, and iPad users with Apple Pencils switch between input modes constantly. CSS media queries can help detect these scenarios:

/* Only apply hover cursors when hover is available */
@media (hover: hover) {
  .button {
    cursor: pointer;
  }
}

/* Adjust for coarse pointers (touch) */
@media (pointer: coarse) {
  .resize-handle {
    min-width: 44px; /* Larger touch targets */
  }
}

Accessibility impact

Screen readers and keyboard navigation automatically take off the cursors. Always make sure your interface works with and without cursor feedback. You can do that by testing with keyboard-only navigation.

This multi-modal approach ensures your cursors enhance the experience without becoming a dependency. Going forward, we will see how to use the cursor property in a real project.

How to use the cursor property

We encounter cursor changes every day while browsing — often without even noticing. For instance, on macOS, when you hover near the edge of a window, the cursor changes to a resize arrow, hinting that the window can be resized. No clicking needed; the intent is clear.

Let’s look at some real-world use cases where cursor styles shape user interactions:

Draggable and resizable panels

In the example below, multiple cursor styles work together:

See the Pen
Draggable & Resizable CSS-Cursor
by Emmanuel Odioko (@Emmanuel-Odioko)
on CodePen.

Take a look:

  • cursor: move; on the header indicates it can be dragged
  • cursor: e-resize; s-resize; and se-resize; are used on the right edge, bottom edge, and corner, respectively, to suggest horizontal, vertical, and diagonal resizing

What exactly is happening under the hood?

While CSS is used to handle the cursor feedback, JavaScript is employed to manage the actual drag and resize behavior. You can implement this with vanilla JavaScript using mouse events (mousedown, mousemove, mouseup) or use libraries like Interact.js or React DnD for more robust functionality:

// Basic vanilla JS approach
element.addEventListener('mousedown', startDrag);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDrag);

// Or with Interact.js (simpler API)
interact('.draggable').draggable({
  listeners: { move: dragMoveListener }
});

The cursor styles tell users what they can do, while JavaScript actually makes the dragging work. When users see cursor: move, they know they can drag, then JavaScript handles the movement when they click and drag.

Interactive buttons with pointer cursor

Here’s a CodePen for the pointer cursor:

See the Pen
Interactive Buttons with Pointer Cursor
by Emmanuel Odioko (@Emmanuel-Odioko)
on CodePen.

This example uses:

  • cursor: pointer; on buttons to indicate clickable elements (this hand-shaped cursor is generally known to indicate clickable interactivity)
  • cursor: not-allowed; on a disabled button, represented by a circle with a slash, clearly indicating no interaction is possible

In many frameworks like React (especially with React 19’s async behavior), the useFormStatus hook is commonly used to manage loading states during form submissions, where cursor: not-allowed is applied to disable user interaction while the form is being processed.

This feedback is typically paired with other styles like reduced opacity and color changes to reinforce the disabled state.

Loading state with wait cursor

Here is an example of a loading state CodePen. I’d suggest you interact with it to get the best of this section:

See the Pen
Loading State with Wait Cursor
by Emmanuel Odioko (@Emmanuel-Odioko)
on CodePen.

In this example, the cursor: wait style is applied using an .is-loading class. It shows a spinning cursor to let users know something is in progress.

This visual cue is supported by other elements like a spinner animation and text changes, common in real project loading states.

Text editing with the text cursor

This is a CSS cursor text example CodePen demo:

See the Pen
Text Editing with Text Cursor
by Emmanuel Odioko (@Emmanuel-Odioko)
on CodePen.

Here, cursor: text; is used on both input fields and a custom div. This I-beam cursor means that text in that area is editable.

Although most browsers apply this cursor by default to input fields, it’s important to explicitly set it for custom editable areas to maintain usability.

Scrollable gallery with grab/grabbing cursors

Let’s look at this one here:

See the Pen
Scrollable Gallery with Grab/Grabbing Cursors
by Emmanuel Odioko (@Emmanuel-Odioko)
on CodePen.

The demo uses two cursor styles:

  • cursor: grab; when hovering over the draggable area
  • cursor: grabbing; when the user actively drags

When the user clicks down, JavaScript switches the cursor from grab to grabbing, changing from an open hand to a closed fist that indicates active dragging is in progress. This pair of cursor styles forms a natural metaphor for physical interaction, like grabbing and moving a physical object.

Can you create custom cursors with CSS?

Yes! You can create custom CSS cursors. Here’s the basic syntax to use custom images as cursors:

cursor: url(path-to-cursor-image) x y, fallback-keyword;

This would look something like this in a project:

cursor: url(custom-cursor.png) 10 15, auto;

Here’s what each component means:

  1. url(path-to-cursor-image) — Path to your custom cursor image file
  2. x y — Optional coordinates that define the exact point that registers clicks
  3. fallback-keyword — A mandatory fallback cursor that displays if the image fails to load

You can also specify multiple cursor images separated by commas:

cursor: url(first-image.png) 5 5, 
         url(second-image.svg),
         url(third-image.cur) 0 0,
         fallback-keyword;

In the code above, the browser tries each cursor in order and falls back to the keyword if none load.

Creating custom cursor files

For developers looking to create custom cursors without starting from scratch, there are several free resources available to help:

Most image editors like GIMP, Photoshop, or even online tools can export .cur files. Just remember to keep cursors small (32×32 or 16×16 pixels) for best performance and cross-browser compatibility.

Drawbacks of the CSS cursor property

Despite its usefulness, the CSS cursor property has a few limitations:

  1. Browser inconsistencies — Cursors can appear differently across Chrome, Firefox, Safari, etc. Some values, like grab, may even require fallbacks on older browsers
  2. Minor performance problems — Large or frequently changing custom cursor images can introduce lag to your site, especially on lower-end devices
  3. User expectation issues — Using cursor: pointer; on non-clickable elements (or vice versa) could break a user’s expectations, though small, but it could affect the user’s experience, especially fellow developers
  4. Accessibility concerns — Cursor changes don’t help users relying on screen readers or keyboard navigation. Beyond screen readers, users with motor disabilities who use alternative input devices may not see cursor changes at all.

I recommend you always pair cursor styles with other indicators:

  • Use aria-disabled="true" alongside cursor: not-allowed for disabled elements
  • Include role attributes where appropriate (e.g., role="button" for clickable divs)
  • Add aria-label or aria-describedby to provide context for complex interactions

Keyboard-visible focus styles as cursor alternatives

For users navigating with keyboards, focus indicators serve the same role that cursor changes do for mouse users. They signal which element is currently active and what can be interacted with:

CSS
.interactive-element {
  cursor: pointer;
}

.interactive-element:focus-visible {
  outline: 2px solid #4f46e5;
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.1);
}

.disabled-element {
  cursor: not-allowed;
  opacity: 0.6;
}

.disabled-element:focus-visible {
  outline: 2px solid #ef4444;
  outline-offset: 2px;
}

The:focus-visible pseudo-class ensures focus rings only appear for keyboard users, not mouse clicks. This creates a smooth interaction system where cursor affordances guide mouse users while focus styles guide keyboard users.

These are all minor limitations, but the cursor property remains very useful when used thoughtfully. The trick here is to use it as a UI enhancement rather than the main or only measure of functionality.

Browser compatibility for CSS cursor property

The CSS cursor property has great overall support in modern browsers. Basic values like pointer, default, text, and wait have almost global support across all modern browsers. In fact, support goes back to Internet Explorer 4, making this one of the most reliable CSS properties available.

For full compatibility details, check the MDN Docs.

Conclusion

We’ve looked into how the CSS cursor property enhances interaction. The cursor property was one of the first CSS features that excited me when I started learning. It was one of the “aha!” moments that made CSS feel interesting.

I will leave you with this: Using cursor: none to hide the pointer might work for immersive games, but it’s disorienting and confusing in regular UI contexts.

Cursor spoofing, where developers mimic system cursors or create deceptive cursors for phishing attempts, breaks user trust and can be misused. Perhaps most frustrating is the misleading cursor problem: using cursor: pointer on non-clickable elements or cursor: grab on content that can’t actually be dragged. These false signals could frustrate a user.

Whether you’re new to CSS or a seasoned dev, remember that cursor styles should enhance, not confuse. When users can trust your visual cues, they spend less time guessing and more time engaging with what really matters in your application.

If you’re curious about custom cursors, check out this detailed guide on creating custom CSS cursors. Keep coding — and keep those cursors honest!

The post How to use the CSS <code> cursor </code> property appeared first on LogRocket Blog.

 

This post first appeared on Read More