Dram's guide to custom cursors in Webflow

Seeing how this is a recurring question on forums and based on overall popularity of the unique cursors in design in the last years i decided to cash in on the fad write this humble beginner’s guide to creating your own beautiful cursors entirely in Webflow, with built-in interactions and only a line of custom style code.

There are a few topics where @vincent, @sabanna, and @anthonysalamin explain how to use custom cursors by inserting your images into code so I’ll speak about pure Webflow implementation only.

Using native Webflow tools will allow for great flexibility in what you can do, and your imagination is going to be the only limiting factor.

To summarize we’re going to:

  • style our own cursor element
  • use ix2 to make it follow the mouse/touchpad movements
  • make sure the element won’t interfere with other interactive elements on the page
  • create hover states for our custom cursor using ix

Here is the project for anyone to take a look, clone and do whatever he wants with it.

So that’s the gist of it. Anyway, let’s begin, shall we?

We will be creating this ubiquitously present cursor just because everyone saw it somewhere and it is a good start for further experimentation.


Setting up the scene and creating our elements

  1. Create a cursor-wrapper div with the following parameters: display: flex, align: center, justify: center, position:fixed, and alignment full, z-index: 100 (this has to be the highest number on your site). This div will contain our always-on-top cursor. We centered its children so that we may create our interaction for following the pointer device.

  2. Create an actual cursor object. Since we are going to create a slightly complex cursor, the object will consist of two elements - inner dot and outer circle.

    Inner dot is a div with the parameters of width and height being 10px, some bright color like #f07 (or whatever you like of course, it’s your cursor after all!), border-radius: 50% for the perfect roundness.

    Outer circle is a div with the following parameters: width and heigth: 40px, position: absolute (do not align it in any way or the centering will be off), border-radius: 50%, set borders to solid 2px line with the color of #f07 (we may make it transparent so that it doesn’t get in the way too much).

OK, the beautiful cursor is ready to be used!


  1. We will create an interaction that will trigger for the whole page. The necessity for using this method rather than on-element interaction will be clear later.


    We will have to use two mouse movement interactions to make both our elements move after the ‘real’ cursor. This is somewhat inconvenient in that we will have to manually add these two interactions onto every page of our website but I do not expect you to add such a playful interface to some 100-pages large corporate site but make use of it sparingly on smaller promo-like projects, so it shouldn’t be that much of a hassle.

  • First interaction that we will create will affect our dot element. Create an interaction, add move transform to the x actions, set its 0% position to move element by x axis -50vw, set its 100% position to move 50vw by x axis. This will ensure the correct movement from left to right in the window. Repeat these steps for y actions but set movement for y axis 0% position to -50vh and y axis’ 100% position to 50vh - this will make our dot move correctly after our pointing device vertically.

    Important thing to note here is that you need to set your interaction to affect class rather than element so that it can be reused without issues on other pages.


    OK, the inner settings in place, now exit the interaction’s config and set its overall smoothing settings to 0% since our dot should ideally be very responsive and not lag behind the actual mouse or touchpad movement.


  • Second interaction will affect our circle. Exit to the very first screen of the ix panel and again choose mouse move in viewport trigger.


    Now you may simply duplicate our first interaction. Click ellipses and choose “duplicate”.


    Enter this new duplicated interaction. Click the uppermost action, then hold shift key and click the bottom action. You will select everything in the config panel, now right-click and select change target, then click the circle element so that everything in the config now affect it instead of our dot. We do not need to modify anything else, the follow rules are the same for the circle. Do not forget to make interaction to affect class rather than element again!


    This time to make our cursor behave pleasantly elastic we will actually increase ix smoothness setting to something like 70%. Done! Exit interactions tab and let’s continue.

Making our default cursor disappear.

Now we have a nice moving object following our cursor but we wanted it to be our cursor not just follow it!

  1. Select your body class in the navigator, go to “style” tab, click the “selector” field and select “Body (All Pages)” dropdown item.


    Now scroll to the very bottom of the styles panel and select “cursor” option. Select “none” from the list. You are done, your newly created cursor element is now your main cursor!


Making sure the cursor elements do not interfere with the interactive objects on the page

Since our cursor elements are located inside the wrapper that is fixed, fills the whole screen, and is above everything else on the page with the highest z-index, we need to do something to be able to hover and click through it to get to the rest of the elements on the page! And this is actually possible with just one line of custom css.

  1. Create an html embed element somewhere on the page and put the following code inside:

    .cursor-wrapper {pointer-events: none;}

    That’s it, we added special property to our cursor wrapper that tells it to not react to any pointer activities like hovers and clicks (this is the reason we had to create our mouse movement interactions for the whole page rather than for this wrapper since it won’t register us hovering over it anymore). Do not forget to copy this html embed element into every page on the site with this cursor.

Adding hover states for links

Now we have a pretty but non-interactive cursor that won’t change no matter what we hover it over. The links will simply produce the default finger-cursor when we hover over them. That’s boring.

  1. Create and select a new link. From the selector dropdown select “All Links”.


    Same as with our body navigate to the bottom, choose “none” from the list of cursors. This will prevent default cursor to appear on links hover.

  2. Now we will add an interaction that will affect our cursor when we hover over a link. For this to work we need to give all links that will have this hover some class. Let’s give our link a class of link-hover-ix.

  3. Having that element selected, go to the interactions panel and create a new ‘mouse hover’ interaction. Now you will define what happens when you hover over any object with the class link-hover-ix and what happens when you hover out of it. For example we can make our dot expand on hover in size to 40px and reduce its opacity to 50%, while our circle will simply vanish with the opacity set to 0%. Set the duration to something like 0.2s and add ease. On hover out we can make our dot to have its size reduced back to 10px and opacity restored to 100%, while our circle will also regain its 100% opacity.

    You should note that selecting elements for use in the interactions will become a bit less convenient since you cannot click it now directly on canvas - the downside of us adding the no pointer events to the element’s wrapper. You will have to click navigator tab, select element from the list and go back to interactions and then configure this element.

    This newy created interaction should be set to affect class rather than element so that we can easily reuse this class to add interaction to any link in the future.

  4. Now simply select any link and add the link-hover-ix class to it. Just make sure to be careful and not style this unique class with any effects - it is now our utility class just for adding interaction.

Disabling custom cursor from appearing on mobile devices

Updating the guide here to make sure we switch our cursor off when viewed on mobile devices - nobody want that weird element just floating around and following our taps (or maybe someone will find a use for such behaviour!).

  1. Switch your designer to the “tablet” view, select our cursor-wrapper and set display: none on the styles panel. OK, no more custom cursor on smaller screen devices (and no more custom cursor on resized browser windows as well but oh well).

    Now once again select your body class in the navigator, go to “style” tab, click the “selector” field and select “Body (All Pages)” dropdown item. Now scroll to the very bottom of the styles panel and select “cursor” option. Select “default” from the list.

    Next select some link without any classes (the clear link is necessary because otherwise webflow designer won’t allow us to go deeper into the selector tree on any other than desktop breakpoint). From the selector dropdown select “All Links”. Now again scroll to the bottom of styles panel and select “pointer” for the cursor.


    The default behaviour for smaller screens is now restored!

  2. Fixing iPad Pro. The iPad Pro is weird since it has a really large resolution so that even if you think you are making your layout for desktops, and tablets should look different, the site will still look and behave like desktop on Pros. You may simply disregard your iPad Pro userbase, which is likely tiny anyway, or use this custom code to target exactly this single device and disable our custom cursor on it (put it into either our embed or into the site settings Head custom code. Do not forget to wrap in <style></style> if doing the latter):

    @media only screen 
      and (min-device-width: 1024px) 
      and (max-device-width: 1024px) 
      and (orientation: portrait) 
      and (-webkit-min-device-pixel-ratio: 2) {
    body {
        cursor: default;
    .cursor-wrapper {
        display: none;
    a {
        cursor: pointer;
    @media only screen 
      and (min-device-width: 1366px) 
      and (max-device-width: 1366px) 
      and (orientation: landscape) 
      and (-webkit-min-device-pixel-ratio: 2) {
    body {
        cursor: default;
    .cursor-wrapper {
        display: none;
    a {
        cursor: pointer;

Well, basically we are finished. We have our hip cursor pointer which smoothly follows our movement, reacts to hovers and let us select text and do whatever we like with the rest of the site.

I hope you will find this easy enough to follow and put this technique to good use. But please do not overdo this! As any good designer you know of course that we should not confuse our users with weird interface elements that significantly change their work environment. I think creating this effect for your strikingly beautiful and unusual promotional page is ok, but putting it into the user interface of a web app that should be used daily is a no no.

Have a good day!


The rule “Don’t make me think” should always be followed unless you intend to confuse. :slight_smile:

Here is what I think: if my aim is to gently and smoothly lead my users through this or that experience without causing any unnecessary imbalance in their perception of universe, I will do my best to make everything cozy and familiar. But if I want to shook them up a bit and make them look at something more intently I can create a slight roadbump so to speak to attract their attention. The size of this roadbump should be carefully evaluated though. Nobody likes being in a road accident, eh?


Right on! That also means the designer needs to understand the website audience or prospective one thoroughly. Which I have no doubt, you take the time to as well.

Lovely post by the way. Thanks for sharing.

1 Like

It took me way more than I expected to write down considering its seemingly trivial nature.


Thanks for taking the time man! :webflow_heart:

That’s great Alex! The result works really well, impressed!


I was surprised not seeing this implemented before (or very infrequently). And I am yet to run into any kind of issues with this approach.

Here’s mine :smile: https://ix2-custom-cursor.webflow.io/


:clap:t3: Wow, what a great approach! And cursor is actually pretty cute :smile:
Thanks for taking time and writing this tutorial :bowing_woman:t3:


You are a star, mate! Cheers!


It was my pleasure, thank you!

I updated the guide to include the necessary stuff to do to make sure the functionality is not changed for mobile devices and added iPad Pro fix, which unfortunately would make my boasting of “just a line of code” somewhat untrue!


This is so brilliantly simple and simply brilliant!!! Amazing work @dram and good writeup.


I love this so much, haha <3 thank you so much for sharing :slight_smile:

1 Like

And I see you have used it in your site already! Just don’t forget to do the #6 or you will have a default pointer on links!

edit: oh, and don’t forget to add the cursor and interactions to ALL your pages or your visitors will end up with no cursor at all!

1 Like

Yeah, I’m actually implementing it right now, so I’m still adding it to other pages hahaha :slight_smile: <3 thanks again!

Thank you for this! @dram

1 Like

Hey guys,

do you have any idea how to turn off a default cursor on Google Maps element? Nothing seems to work for me.

You are welcome, mate.

@piotr_k Hm… the cursor property in google maps is set inline for one of the unnamed inner map divs. No idea how to target that one honestly. But why would you want to hide a cursor on google map object? Even with custom one it will be not very convenient and plain awkward without the familiar interaction from the map for users. If you want the custom cursor to not interfere with the default that is visible on google maps you may make your map object higher on the z-index than the cursor so that custom cursor disappears behind it and default takes stage.

1 Like

I wanted to make consistent look on the page. But anyway, thanks for your clever idea about z-index positioning - it seems to be the most reasonable solution to this problem so far.

Just don’t forget that your map will now go over your fixed nav if you have one! Also turn z-index down in the smaller viewports just in case!