Cool Custom Cursors With React + Framer Motion: Part 1

Levon Arakelyan
The Startup
Published in
4 min readAug 30, 2020

--

I am constantly surprised and inspired by so many websites on awwwards.com, many of which frequently feature cool cursors and various effects.

In these series, I’ll break down the process of making custom cursors and try to experiment with various cool visual effects based on them, all using React.js and Framer Motion.

Basic Custom Cursor

First, let’s start by defining some basic structure and styling for our custom cursor, which will just be a black circle and be placed in a fixed position at the top: 0; left: 0; corner for now.

function App() {
return (
<div className="App">
<div className="cursor" />
</div>
)
}
---------------------------------.cursor {
position: fixed;
left: 0;
top: 0;
width: 32px;
height: 32px;
border-radius: 16px;
background-color: black;
}

Now that we have the custom cursor let’s bring it to life with some JavaScript. First, we need to capture the mousemove events and define a listener function which will animate our cursor to a correct position.

Since we’ll be using a functional component we need to define our listener in a useEffect hook.

import React, { useState, useEffect } from "react"function App() {
const [cursorXY, setCursorXY] = useState({ x: -100, y: -100 })
useEffect(() => {
const moveCursor = (e) => { }
window.addEventListener('mousemove', moveCursor) return () => {
window.removeEventListener('mousemove', moveCursor)
}
}, [])
return (
...
)
}
...

The return function in the useEffect removes the listener on cleanup and makes sure that we don’t add multiple event listeners for the same event. The last argument given to useEffect is an empty array [], which means we’ll be only running the function once over the component’s lifecycle, since it doesn’t depend on any props.

As you can see we’ve also used useState to define the initial coordinates for our cursor, which we’ll change later when we switch to using some framer-motion magic 🧙‍♂️.

Now let’s complete our moveCursor function and apply some transforms to our cursor element to make it follow the pointer position.

useEffect(() => {
const moveCursor = (e) => {
const x = e.clientX - 16
const y = e.clientY - 16
setCursorXY({ x, y })
}
...<div
className="cursor"
style={{
transform: `translate3d(${cursorXY.x}px, ${cursorXY.y}px, 0)`,
}}
/>
...

Here we’re getting current cursor position from e.clientX and e.clientY and subtracting 16px which is half the size of our custom cursor, so that its center is aligned with the correct pointer position.

Now that we have our custom cursor fully working, we just need to get rid of the default system cursor, which can be easily done with this:

* {
cursor: none;
}

One last touch to make it fully usable is to set pointer-events: none; to make sure our custom cursor element doesn’t interfere with mouse events on other DOM elements.

Adding Framer Motion

OK, now let’s make things more interesting by adding framer-motion and some effects into the mix.

Instead of using setState to keep out cursor position, lets import and use useMotionValue from framer-motion:

...
import { motion, useMotionValue } from "framer-motion"
function App() {
const cursorX = useMotionValue(-100)
const cursorY = useMotionValue(-100)
useEffect(() => {
const moveCursor = (e) => {
cursorX.set(e.clientX - 16)
cursorY.set(e.clientY - 16)
};
...

… and update our div to a motion.div:

<motion.div
className="cursor"
style={{
translateX: cursorX,
translateY: cursorY,
}}
/>

Now, lets say we want to give some springiness effect to our cursor motion. We can use a great utility from framer-motion called useSpring. We’ll take our MotionValues and pass them to useSpring to apply some spring physics to the motion like this:

...
const cursorX = useMotionValue(-100);
const cursorY = useMotionValue(-100);
const springConfig = { damping: 25, stiffness: 700 };
const cursorXSpring = useSpring(cursorX, springConfig);
const cursorYSpring = useSpring(cursorY, springConfig);
...<motion.div
className="cursor"
style={{
translateX: cursorXSpring,
translateY: cursorYSpring,
}}
/>
...

Note that you can get a lot of various behaviors just by specifying different spring damping and stiffness values, so try experimenting with the spring configuration.

One other popular effect is to give our cursor a negative color based on the background underneath it. We can achieve that by applying this cool CSS to our .cursor:

mix-blend-mode: difference;
background-color: white;

Just make sure you have specified a suitable background-color on body or the element under the cursor so that you can see the cursor. For example if you want the cursor to appear black on a white background you actually have to apply white background to both the background element and the cursor itself, since it has a difference blending mode.

That’s about it folks for this part. Stay tuned for the Part 2 of the series where we’ll make our custom cursor more “intelligent”, interactive and experiment with some even cooler visual effects!

For the final working example check out this link and be sure to check out the repo on GitHub.

--

--

Levon Arakelyan
The Startup

Design, Coding, Engineering, Photography, Acoustics