Cool Custom Cursors With React + Framer Motion: Part 1
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.