Editor’s note: This article was last updated by Njong Emy on 5 August 2024 to update content and code blocks, as well as to offer a brief comparison of CSS and JavaScript cursors to understand scenarios when you would want to use JavaScript.
Acting as the middleman between your users and your website, cursors can either limit or greatly enhance the way your users experience your site. This is why sleek, intentionally designed, custom cursors have become a significant part of UI and UX today.
Custom cursors are an opportunity to give your users direction in an engaging way and create a memorable, immersive experience for them on your website.
In this tutorial, we’ll take a look at what custom cursors are and learn how to use CSS (and JavaScript) to create custom cursors that will give your website a creative edge. To follow along with this tutorial, you should have some knowledge of HTML, CSS, and JavaScript.
We already interact with custom cursors every day. When you hover over buttons and the pointer cursor changes to a hand, or you hover over some text and the cursor changes to a text cursor, this interactivity is achieved through custom cursors.
However, there are many other creative experiences we can provide to our users with cursors. Before we dive into creating custom cursors, you should know that CSS provides you with cursors out of the box for some frequently performed tasks.
These cursors show you what can be done at the exact location you are hovering over. Examples include cursors indicating that you should click links, drag and drop elements, zoom in and out on things, and more.
All you have to do is specify the type of cursor you want using the CSS cursor
property. Some of the available cursors in CSS include:
.auto { cursor: auto; } .default { cursor: default; } .none { cursor: none; } .context-menu { cursor: context-menu; } .help { cursor: help; } .pointer { cursor: pointer; } .progress { cursor: progress; } .wait { cursor: wait; } .cell { cursor: cell; } .crosshair { cursor: crosshair; } .text { cursor: text; } .vertical-text { cursor: vertical-text; } .alias { cursor: alias; } .copy { cursor: copy; } .move { cursor: move; } .no-drop { cursor: no-drop; } .not-allowed { cursor: not-allowed; } .all-scroll { cursor: all-scroll; } .col-resize { cursor: col-resize; } .row-resize { cursor: row-resize; } .n-resize { cursor: n-resize; } .e-resize { cursor: e-resize; } .s-resize { cursor: s-resize; } .w-resize { cursor: w-resize; } .ns-resize { cursor: ns-resize; } .ew-resize { cursor: ew-resize; } .ne-resize { cursor: ne-resize; } .nw-resize { cursor: nw-resize; } .se-resize { cursor: se-resize; } .sw-resize { cursor: sw-resize; } .nesw-resize { cursor: nesw-resize; } .nwse-resize { cursor: nwse-resize; }
Hover over the boxes below to see the cursors in action:
See the Pen
Untitled by Samson Omojola (@Caesar222)
on CodePen.
Check out the complete list of CSS cursors here.
While these cursors are useful and have some basic styling, we can certainly get more creative.
Creating a custom cursor with CSS is a pretty straightforward process. The first step is to find the image you want to use to replace the default cursor. You can either design one yourself or get a free PNG that suits your needs from an icon library such as FontAwesome.
Next, point the CSS cursor
property to the location of the image using url
. Now, the cursor
property knows that it’s meant to use whatever image is at that URL as its cursor:
body { cursor: url('path-to-image.png'), auto; }
To ensure that this cursor is used on all parts of your website, the best place to use the cursor
property is in the body
tag of your HTML. However, if you want, you can assign custom cursors to specific elements instead of the whole website.
You can also add a fallback
value to your cursor
property. When using custom CSS properties, this value ensures that if the image that serves as your custom property is missing or cannot be loaded, then your users will have another option.
In this case, auto
is the fallback
descriptor for your custom cursor
property. Your users will see the regular cursor if the custom one is unavailable.
You can also provide more than one custom cursor (multiple fallbacks) for your website. All you have to do is add their paths to the cursor
property:
body { cursor: url('path-to-image.png'), url('path-to-image-2.svg'), url('path-to-image-3.jpeg'), auto; }
There are three fallback cursors in the code above.
Because they draw attention to elements you want to highlight on your website, custom cursors are best used in specific scenarios, such as:
Say you have a table and you’d like the mouse cursor to change to a pointer (i.e., the hand icon) whenever a user hovers over a row in the table. You can use the CSS cursor
property to achieve this.
Here’s an example:
<style> /* Style the table */ table { font-family: arial, sans-serif; border-collapse: collapse; width: 100%; } /* Style the table cells */ td, th { border: 1px solid #dddddd; text-align: left; padding: 8px; } /* Style the table rows */ tr:hover { cursor: pointer; } </style> <table> <tr> <th>Name</th> <th>Age</th> <th>City</th> </tr> <tr> <td>John</td> <td>30</td> <td>New York</td> </tr> <tr> <td>Jane</td> <td>25</td> <td>Chicago</td> </tr> <tr> <td>Bill</td> <td>35</td> <td>Los Angeles</td> </tr> </table>
In the above code, we use the tr:hover
selector to apply the cursor
property to all table rows when the mouse hovers over them. The cursor
property is set to pointer
, which changes the mouse cursor to a hand icon.
To hide the mouse cursor with CSS, you can use the cursor
property and set its value to none
. Here’s an example:
<style> /* Style the body element */ body { cursor: none; } </style> <body> <!-- Your content goes here --> </body>
This will hide the mouse cursor throughout the entire webpage. If you only want to hide the mouse cursor for a specific element, you can apply the cursor
property to that individual element instead of the body
element.
There are several situations in which hiding the mouse cursor might be useful, such as:
Remember that hiding the mouse cursor can be confusing or disorienting for some users, depending on the use case. This strategy should be used carefully and only when necessary.
In as much as custom cursors can be created with CSS, using JavaScript also has its added advantages. Before we dive into how to create custom cursors with JavaScript, let’s take a look at the advantages and disadvantages of creating custom cursors with CSS, and the same for JavaScript.
There are numerous reasons why it is preferable to create cursors with CSS:
The main disadvantage of using CSS for custom cursors is that adding animation or further customizing the cursor would involve more complexity. This is where JavaScript comes in. With JavaScript, you can add more complex interactions when the user interacts with the cursor, i.e., hovering, clicking, or moving over specific elements. By listening to specific events, the cursor’s movements can then be updated. Cursors can also be more easily animated with JavaScript. With only CSS, creating complex cursor transitions may prove to be more difficult.
Just as simplicity and browser support are reasons why you should use CSS, complexity and cross-browser issues are reasons why you shouldn’t use JavaScript.
Creating a custom cursor with JavaScript involves manipulating DOM elements. We’ll create some DOM elements, which will serve as our custom cursor, and then use JavaScript to manipulate them. Then, as we move our cursor around, those custom elements will move around as our cursor.
Rather than design an image or download an image online, we will use CSS to design a custom cursor. I want us to use something animated that draws users in. Move your cursor around the box below to see an example of what I’m describing:
See the Pen
Untitled by Samson Omojola (@Caesar222)
on CodePen.
As you can see, the cursor consists of two elements — one large circle and one small one. We’ll create two div
elements and give them class
names:
<div class="cursor small"></div> <div class="cursor big"><div>
Next, we’ll create the circles with CSS. In the code below, we assign a width and height of 50px
each to the big circle. To make it a circle, we give it a border radius of 50%
.
The small circle will be hollow, so we give it a border and border-radius of 50%
. Then, we assign it a width and height of 6px
each.
We disable the default cursor by giving cursor
a value of none
so that we can render the custom cursor in its place.
To add animation to the big circle, we use @keyframes
.
Our animation-duration
is 2.0s
. At the start of this duration, we set background-color
to green
and opacity
to 0.2
. Halfway through, we set the circle’s background-color
to orange
. At the end of the 2s duration, we set the circle’s background-color
to red
.
To make the animation repeat over and over again, we set animation-iteration-count
to infinite
:
body{ background-color: #171717; cursor: none; height: 120vh; } .small{ width: 6px; height: 6px; border: 2px solid #fff; border-radius: 50%; } .big{ width: 50px; height: 50px; border-radius: 50%; margin-bottom: 20rem; animation-name: stretch; animation-duration: 2.0s; animation-timing-function: ease-out; animation-direction: alternate; animation-iteration-count: infinite; animation-play-state: running; } @keyframes stretch { 0% { opacity: 0.2; background-color: green; border-radius: 100%; } 50% { background-color: orange; } 100% { background-color: red; } }
Now, to make the elements move as you move your mouse, we’ll use JavaScript.
In the code below, we use an event listener to listen for whenever a user moves their mouse over our webpage. Whenever this event takes place, we use a function to get the x
and y
coordinates of the mouse. We then use the x
and y
coordinates to move our div
elements around as a representative of our cursor:
const cursorSmall = document.querySelector('.small'); const cursorBig = document.querySelector('.big'); const positionElement = (e)=> { const mouseY = e.clientY; const mouseX = e.clientX; cursorSmall.style.transform = `translate3d(${mouseX}px, ${mouseY}px, 0)`; cursorBig.style.transform = `translate3d(${mouseX}px, ${mouseY}px, 0)`; } window.addEventListener('mousemove', positionElement)
See the complete code alongside the interactive cursor in the below CodePen:
See the Pen
Untitled by Samson Omojola (@Caesar222)
on CodePen.
As you can see, we used querySelector
to get access to the two div
elements on the DOM. Then, we added an event listener to the webpage to listen for and detect when the user moves their mouse.
When this happens, the event listener triggers a positionElement
function. In this function, we mapped these coordinates to the div
elements whose DOM positions we have already selected.
We move the div
elements in correspondence with the x
and y
coordinates of the mouse using transform
and translate3d
. transform
repositions elements in horizontal and vertical directions, while translate3d
repositions elements in 3D space.
As the mouse moves, its x
and y
values change, transform
and translate3d
get updated with the help of the event listener, and the position of each div
changes in correspondence.
When creating a custom cursor, you should always keep the user’s best interests in mind. One way to do this is by creating a cursor that best represents or translates the essence of an element. While custom cursors give your website a unique touch, it is important to not overdo it. Overcustomizing your cursor can frustrate and confuse your users by complicating their experience on your website.
Before you decide to create a custom cursor, you should consider its functionality. For example, will a custom cursor’s features work well on an old browser?
Note that some older browsers can’t support many new CSS and JavaScript features. If you design certain areas of your website around a custom cursor that uses technologies beyond your users’ browsers, they won’t be able to engage with your website.
As mentioned previously, custom cursors can be helpful for users with impaired mobility and other accessibility requirements. For example, people with low vision may need very large mouse pointers that are easy to track or mouse pointers with high-contrast colors that make them stand out from various backgrounds.
A mouse can also be programmed to invert the colors of any elements it hovers over. This makes it easy for users to track what they’re reading or seeing on the screen. The text cursor or caret can also be thickened so that users with low vision don’t lose track of where the cursor is on the page while typing.
Large cursors can also benefit users with motor impairment, as they are easier to move and place on elements than small cursors.
Some users may prefer to keep the default cursor for familiarity. The use of media queries such as prefers-reduced-motion
can be used to toggle or disable custom cursors for users who find them distracting:
@media (prefers-reduced-motion: reduce) { *{ cursor: auto; /*reverts to the default cursor */ } }
Another important technique to improve accessibility is by disabling custom cursors for screen readers. These may interfere with screen readers and create confusion for the user. Adding aria-hidden = "true"
to the elements that make use of the custom cursor, ensures that they are not exposed to assistive technologies like screen readers, thereby preventing confusion.
In this tutorial, we learned about CSS cursors available out of the box, how to create custom cursors with CSS, how to give your webpage multiple cursors, and how to use animation in CSS and JavaScript to create custom cursors. We’ve also seen the pros and cons of using CSS and JavaScript when handling custom cursors, and when to make use of cursors that are not the default.
Custom cursors can be a great way to draw your users in, keep them engaged, and direct them efficiently if implemented correctly. If you have any further thoughts or questions about creating custom cursors with CSS, let me know in the comment section.
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 is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
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 nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
4 Replies to "Creating custom mouse cursors with CSS"
useful probably only if you copy the example itselft
in ‘real world’ the custom cursor if far away from the actual one
Use proper apostrophes. Don’t use url(‘path-to-image.png’), use url(‘path-to-image.png’) instead!
Thanks for catching those, we’ve updated the code
You wrote: “Custom cursors can be a great way to draw your users in, keep them engaged…”
Here. I’ll fix it for you:
“Custom cursors can be a great way to draw your users in, keep them ENRAGED.”
Please don’t use them. They’re annoying and counterproductive.