cursor
propertyThe 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.
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.
cursor
property?The CSS cursor
property 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.
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 */ } }
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.
cursor
propertyWe 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:
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 draggedcursor:
e-resize
; s-resize
; and se-resize
; are used on the right edge, bottom edge, and corner, respectively, to suggest horizontal, vertical, and diagonal resizingWhile 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.
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 possibleIn 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.
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.
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.
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 areacursor: grabbing
; when the user actively dragsWhen 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.
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:
url(path-to-cursor-image)
— Path to your custom cursor image filex y
— Optional coordinates that define the exact point that registers clicksfallback-keyword
— A mandatory fallback cursor that displays if the image fails to loadYou 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.
For developers looking to create custom cursors without starting from scratch, there are several free resources available to help:
.cur
filesMost 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.
cursor
propertyDespite its usefulness, the CSS cursor
property has a few limitations:
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 developersI recommend you always pair cursor styles with other indicators:
aria-disabled="true"
alongside cursor: not-allowed
for disabled elementsrole
attributes where appropriate (e.g., role="button"
for clickable divs)aria-label
or aria-describedby
to provide context for complex interactionsFor 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.
cursor
propertyThe 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.
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!
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket lets you replay user sessions, eliminating guesswork around why bugs happen by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings — compatible with all frameworks.
LogRocket's Galileo AI watches sessions for you, instantly identifying and explaining user struggles with automated monitoring of your entire product experience.
Modernize how you debug web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild a React-based AI image generator app offline using the Hugging Face Diffusers library and Stable Diffusion XL.
Get up to speed on Google’s latest breakthrough with the Gemini 2.5 model and what it means for the future of frontend AI tools.
In this article, we’ll explore the best Rust frameworks for web development, including Actix Web, Rocket, Axum, warp, Leptos, Cot, and Loco.
We explore the benefits of building an app with React, TypeScript, and Vite, and compare its performance to the same app built with CRA.