How to create a custom cursor in React Part — 1
Tags: react, javascript, css, html
Published: 2021-06-19 13:41:19

**P.S — No modules were used while writing this code** 😆

If you’re looking for the source code then it’s available in the video 😉

{% youtube VSZhw54yO3I %}

Please support by watching this video 😢

## Basic HTML — CSS layout of the cursor

We’re gonna start with writing the **JSX(HTML)** for cursor dot and cursor outline.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3swrmb3kfhu1i3enb3cn.png)

We also created the `ref` for these elements so that we can access the DOM elements later on.

Next, we’re gonna add the **CSS** styling to these elements.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nnzo98yo8nyytpqb8n8o.png
)

`cursor: none` is added to the universal selector `*` because we want to hide the default cursor.

`pointer-events: none` so that this element doesn’t become a target of the pointer-events. [MDN pointer-events]
(https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events) 

```css
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
``` 

This code snippet is used to move both elements in the centre.

`opacity: 1;` is used so that later on we can hide and show our cursor based on `mouseenter` and `mouseleave` event. That’s why `transition` is also used on the `opacity` property.

Transition on `transform` is used so that we can increase and reduce the size of the dot and outline based on some mouse events (will soon get to know which one 😉).

It’ll look something like this.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubu9ok7sqrleabrv4icz.png)

## Adding images to show cursor hover effect

We’re gonna add some images to show that smooth cursor hover transition.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ncmsvw1b22wwu5jrqq9u.png)

Created this `<Link />` component which can be reused for displaying the images.

Now, we need to import this into our `<Cursor />` component and pass the images. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u0w3sazwn3uputweisbj.png)

We’ve added `ref` to its container because we’ll be adding the mouse events to all those images.

The `getImage` is a util function that I created in order to get the images based on the index value provided by the map function. You can find the function in the source code (not adding any image because it isn’t that important. Sorry `getImage` function! 😥). 

**Bonus Points 🤩**

- **The import statement’s path**

In order to use that I used `jsconfig.json`. You can add this file at the root of your project and add the `baseUrl` property inside `compilerOptions`.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aeam0pmlpzoq77bfhoh5.png)

- **The `[...Array(4).keys()]` code snippet** 
 
The `Array(4)` will create an empty array of size 4.

The **``keys()``** method returns a new **Array Iterator** object that contains the keys for each index in the array.

And spreading the array will give us an array — `[0, 1, 2, 3]`

Okay, let’s get back to what we were doing 😅.

The final thing is to add a bit of styling to our images container.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sdqh9ock2mtp4hyrmrym.png)

And now the final outcome will look something like this —

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6yga51kat9e48ver4g84.png)

## Adding Animation to our cursor

Finally, the time has come to add the animation to our cursor (poor cursor sitting alone at the centre of the screen 😥).

**Adding ref variables**

We need to add some more `refs` to our **Cursor** component so that we can update the changes.  

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odudpqgveq4qxyuti75q.png)

**delay** will be used as a factor by which we will change how quickly or slowly the cursor outline should follow the cursor dot. A higher number means slow and vice versa.

**cursorVisible** will help in toggling the visibility of the cursor.

**cursorEnlarged** will help in toggling the size of the cursor.

**endX** is the X-position of the cursor dot. We will be saving the X position of the dot in this variable. It is initialised with ``window.innerWidth/2`` because that’s mid of X-axis.

**endY** is the Y-position of the cursor dot. We will be saving the Y position of the dot in this variable. The concept is same as **endX**.

**_x** is the X-position of the cursor outline. We will be saving the X position of the outline in this variable. It’s initialised with 0 because we want to show movement animation. (The outline coming from the top left corner of the screen to the middle point where the dot is present at the start of the application).

**_y** is the Y-position of the cursor outline. We will be saving the Y position of the outline in this variable. The concept is same as **_x**.

**requestRef** will save an ID returned from one important function (will see it when we add an animation to the cursor outline). 

**Adding cursor toggle functions**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98rj5236xx61xx5s2n82.png)

- **toggleCursorVisibility** — If the cursor is visible then we will change the opacity to 1 otherwise we will change it to 0. 

- **toggleCursorSize** — If the cursorEnlarged is true then we will decrease the size of the dot and increase the size of the outline. In case of a false, we will change it back to the original size.

**Adding over, out, enter, leave mouse events**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/llxr2c4pp7ocgeoc5yqn.png)

**‘mouseover’** — The **mouseover** event is fired when the cursor comes on top of the element or its children.

**‘mouseout’** — The **mouseout** event is fired when the cursor is not on top of the element or its children.

**‘mouseleave’** — The **mouseleave** event is fired when we leave the page viewport.

**‘mouseenter’** — The **mouseenter** event is fired when we enter the page viewport.

Now, we can pass **mouseOverEvent** and **mouseOutEvent** to our image component. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kp73xmcbt9j3aeieh14y.png)

These functions will be called whenever the mouse cursor comes on top of the image.

**Adding mousemove event** 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0bib52lgss0f0wox0gij.png)


The first thing we need to make sure is that our cursor is visible, that’s why we’ve updated the **currentVisible** and called the **toggleCursorVisibility** function.

Then, we are taking the current position of the cursor with the help of **pageX** and **pageY**. The next step is to update the top and left property of the dot cursor to show it moving around on the page.

**P.S** — This [StackOverflow](https://stackoverflow.com/questions/6073505/what-is-the-difference-between-screenx-y-clientx-y-and-pagex-y) answer has a very good explanation about the difference between **clientX** and **pageX**.

**Adding cursor outline animation**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gjzklaxac396ecszsgk.png)

This is a special recursive function that we call inside ``requestAnimationFrame`` function. In this function, we update the position of **cursor outline** with respect to the position of **cursor dot**.

The mathematical explanation for this is present in part 2 of the article.

This [article](http://www.javascriptkit.com/javatutors/requestanimationframe.shtml) is great to understand how **requestAnimationFrame** works and why it was created.  

**Calling all event functions**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lh98n6c0ri86g8aw0fr3.png)

We can call all our event functions inside `useEffect` (componentDidMount). At the time of unmounting, we should remove all the event listeners and also cancel the requestAnimationFrame using **cancelAnimationFrame** function.

And with all these functions, our poor cursor can finally move 🥳 

![2021-06-13 22-32-53](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7m3f6sjii8gn6pns44r9.gif)

[Part 2](https://germavinsmoke.medium.com/how-to-create-a-custom-cursor-in-react-part-2-mathematical-explanation-2193b52c43ba) of the article is having a little bit of mathematical explanation. So you can check it out if you want to.

Some of the important links —  

MDN requestAnimationFrame — https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

StackOverflow answer on requestAnimationFrame — https://stackoverflow.com/questions/52212703/using-requestanimationframe-in-react/52213728

Using requestAnimationFrame in React — https://css-tricks.com/using-requestanimationframe-with-react-hooks/

Using event listeners in React — https://www.pluralsight.com/guides/event-listeners-in-react-components

VanillaJS codepen for custom cursor — https://codepen.io/kjbrum/pen/qooQJJ