Custom images
cursor: url() in CSS: custom image cursors that work
url() replaces the system cursor with your own image. You give it an image, an optional hotspot and a keyword to fall back on, and the browser does the rest.
cursor: url(studio-arrow-32.png); Move your mouse over this box Cursors only appear with a mouse or trackpad. Open this page on a computer to see this one. What it shows
Your image, at its natural size, with the hotspot where you put it. If the image fails to load, is larger than 128x128, or would stick out of the page area, the browser moves on to the next image in the list and finally to the keyword. A .cur file can carry its own hotspot; PNG and SVG use the coordinates you write, or the top-left corner.
Usage
When to use it
Use it for
- Brand moments: a portfolio, a launch page or a game where the cursor is part of the look.
- Tools that need a cursor the system does not have, such as a brush outline, an eyedropper or a sized eraser.
- A higher-contrast version of a system cursor on a dark or busy background.
- Keep the image at 32x32, offer an SVG or a 2x file for sharp screens, and always end the list with a keyword.
Avoid it for
- Site-wide on content pages, where it mostly gets in the way of reading and clicking.
- Large images. Above 128x128 every browser ignores them, and larger ones are dropped near the edges of the page.
- Animation. Browsers show a single frame of a GIF, and .ani files do not work at all.
Code
Copy the snippet
.brand-zone {
cursor: url("/demo/studio-arrow.svg") 3 2,
url("/demo/studio-arrow-32.png") 3 2,
auto;
} <div class="brand-zone">
Move over this box to see the custom arrow.
</div> Support
Browsers and systems
| Browser | Support |
|---|---|
| Chrome | 1 |
| Edge | 12 |
| Firefox | 1.5 |
| Safari | 3 |
| Safari on iOS | partial: 13.4+, hotspot coordinates ignored |
| Chrome on Android | 18 |
| System | Matching cursor |
|---|---|
| Windows role | any role, via your own .cur or .ani file |
| Windows file | your file |
| macOS | custom image |
Watch out
Gotchas
- The keyword at the end is required. cursor: url(a.png); is invalid, and the browser ignores the whole declaration.
- The hotspot is measured in image pixels from the top-left corner. Chrome and Firefox clamp a hotspot that falls outside the image; Safari ignores it and uses the file's own hotspot or 0 0. Leave it out and every browser uses the hotspot stored in a .cur file, or 0 0.
- Chrome 156 and later, and Safari, drop any custom cursor that would stick out of the visible page. Firefox, and Chrome before 156, do this only for cursors larger than 32x32. Near the top edge or a scrollbar you then see the fallback.
- Give an SVG cursor width and height attributes. Browsers only have to support SVG cursors that state their own size.
- On iPad, hotspot coordinates are ignored.
- Browsers show one frame of an animated image, and no current browser plays .ani files.
FAQ
Questions
- What size should a custom cursor be?
- 32x32 pixels. It matches the system cursor, works in every browser and is not dropped near the edges of the page. 128x128 is the hard ceiling.
- Why does my custom cursor not show?
- The usual reasons: no fallback keyword (which makes the whole rule invalid), a wrong path, an image larger than 128x128, or a cursor that would stick out of the page near an edge. Check that the rule is not crossed out in developer tools, then try a 32x32 PNG.
- Can I use an animated GIF as a cursor?
- You can, but browsers show a single frame. For animation you need a JavaScript cursor that follows the pointer.
More
Related values and sources
Sources
- MDN Web Docs: cursor
- CSS Basic User Interface Module Level 4: the cursor property
- MDN browser-compat-data: css.properties.cursor
- Chromium commit: viewport containment for all custom cursors (Chrome 156)
- Firefox bug 1445844: block large cursors that cover browser UI
- WebKit source: image cursor selection (StyleCursor.cpp)
- Chromium source: 128 px limit for web cursors (cursor.cc)