DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for How to create a custom cursor with React and Framer-motion
Kolapo Damola Usman
Kolapo Damola Usman

Posted on • Updated on • Originally published at kolapo.hashnode.dev

How to create a custom cursor with React and Framer-motion

In this article, I will guide you through the process of creating a custom cursor for your React application using Framer motion. At the end of the tutorial, you will have built a custom cursor that looks like the following:

Custom-cursor-Final-gif

Prerequisite

To follow along with this tutorial, you’ll need the following:

  • A text editor
  • Node.js installed locally on your machine
  • Working knowledge of HTML, CSS, and JavaScript
  • Working knowledge of React

You can find the complete code for this tutorial at this CodeSandbox.

Let’s get started!

Setting up the Project

Now, let’s set up a simple React project and install the necessary dependencies.

We’ll start by installing React:

npx create-react-app custom-cursor-app
Enter fullscreen mode Exit fullscreen mode

Or you can use Yarn

yarn create react-app custom-cursor-app
Enter fullscreen mode Exit fullscreen mode

Next, we’ll install Framer Motion

npm install framer-motion
Enter fullscreen mode Exit fullscreen mode

Or you can use Yarn

yarn add framer-motion
Enter fullscreen mode Exit fullscreen mode

Getting Started

First, we'll open the App.js file and remove some of the code in it, next we'll replace it with these few lines of code

import "./App.css";

const App = () => {
  return (
    <div className="App">
      <h1 className="title">Custom Cursor</h1>
      <div className="cursor"></div>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Next, we’ll import everything else that’s required to help us create a custom cursor, from the libraries we installed earlier:

useState and useEffect Hooks from React.

motion Hook from Framer Motion.

import { useState, useEffect } from "react";

import { motion } from "framer-motion";
Enter fullscreen mode Exit fullscreen mode

Next, open the App.css file and remove the code in it and place these lines of code in the file, this helps us style the App.js file

@import url("https://fonts.googleapis.com/css2?family=Mochiy+Pop+One&display=swap");

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
}

.App {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  background-color: aqua;
  font-family: "Mochiy Pop One", sans-serif;
}

.title {
  font-size: 5rem;
}

.cursor {
  background-color: #111;
  height: 16px;
  width: 16px;
  border-radius: 50%;
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
}
Enter fullscreen mode Exit fullscreen mode

Getting the Mouse position

To get the mouse position when running the app, we'll need the useState and useEffect Hooks provided by React.

import "./App.css";
import { useState, useEffect } from "react";
import { motion } from "framer-motion";

const App = () => {
// state for mouse position
  const [mousePosition, setMousePosition] = useState({
    x: 0,
    y: 0,
  });

  useEffect(() => {
    const mouseMove = (e) => {
      setMousePosition({
        x: e.clientX,
        y: e.clientY,
      });
    };

    window.addEventListener("mousemove", mouseMove);

    return () => {
      window.removeEventListener("mousemove", mouseMove);
    };
  }, []);

  return (
    <div className="App">
      <h1 className="title">Custom Cursor</h1>
      <div className="cursor"></div>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Here, we use the useState Hook to set the state for the mouse position.
In the useEffect Hook, we are getting the current position of the mouse using the window object.
Next, we set the state x and y of the mousePosition using the clientX and clientY method from the event object.
We are now able to track the movement of the mouse on the screen.

Adding Animations

Next up, we'll be animating the mouse cursor using the motion module we imported from Framer motion.

const App = () => {

  // Variant animation
  const variants = {
    default: {
      x: mousePosition.x - 8,
      y: mousePosition.y - 8,
    },   
 };

  return (
    <div className="App">
      <h1 className="title">
        Custom Cursor
      </h1>
// using the motion module to animate the cursor div element
      <motion.div
        className="cursor"
        variants={variants}
        animate="default"
      ></motion.div>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Here, we are targetting the cursor element, we create a variant for the cursor animation, and set the default x and y position of the cursor element using the mousePosition state, mousePosition.x for the cursor element x initial position and mousePosition.y for the cursor element y initial position.
In the App.css file the cursor div element is styled to have a width and height of 16px each, reducing the mousePosition.x and mousePosition.y by 8 helps center the mouse cursor in the cursor div element.
Next up, to remove the native browser cursor, in the App.css file paste this line of code.

*,
*::before,
*::after {
  cursor: none;
}
Enter fullscreen mode Exit fullscreen mode

Setting the Mixblendmode

To set the mixBlendMode for the custom cursor, open up the App.js file and edit the code.


// Set cursor variant to change color on hover text
const [cursorVariant, setCursorVariant] = useState("default");

// Variant animation
const variants = {

// default animation (applies onMouseLeave)
  default: {
    x: mousePosition.x - 8,
    y: mousePosition.y - 8,
  },

// text animation (applies onMouseEnter) 
  text: {
    height: 150,
    width: 150,
    x: mousePosition.x - 70,
    y: mousePosition.y - 70,
    backgroundColor: "aqua",
    mixBlendMode: "difference",
  },
};

// function for textLeave and textEnter
const textEnter = () => setCursorVariant("text");
const textLeave = () => setCursorVariant("default");

return (
  <div className="App">
    <h1 className="title" onMouseEnter={textEnter} onMouseLeave={textLeave}>
        Custom Cursor
      </h1>
     <motion.div
       className="cursor"
       variants={variants}
       animate={cursorVariant}
     ></motion.div>
  </div>
 );
};
Enter fullscreen mode Exit fullscreen mode

First, we create a new state for the cursor animation variant using the useState Hook.
Next, we create a function to set the variant animation of the cursor element to text animation when the mouse hovers over the h1 element, and we have another function to set the variant animation of the cursor element to default when the mouse leaves the h1 element.
Going over the text variant animation, we set the width and height of the cursor element to 150px each, and reduce the mousePosition x and y by 70px each to center the cursor.
Next up we give the cursor element a background color of aqua, so when the mouse enters the h1 element the color changes to aqua.
Finally, we set the mixBlendMode property to difference.
The mixBlendMode property is what inverts the color of the h1 text to the aqua color onMouseEnter.

Your complete custom cursor webpage should look like this:

Custom-cursor-Final-gif

You can find the complete code for this tutorial at this CodeSandbox.

Conclusion

Framer motion is an extremely helpful library to use when you want to create animations. You can find a full list of Framer-motion utilities in the animation section of the documentation.

Thanks for reading, and have fun playing around with this and make some tweaks to get a better custom cursor for your webpage!

If you enjoyed reading this as much as I enjoyed writing it, then Like and Share this with your friends and feel free to connect with me on Twitter πŸ‘¨β€πŸ’».

Buy Me A Coffee

Top comments (1)

Collapse
 
tazdecoder profile image
Tarwat

Just checked out the code sandbox; this looks very promising!

I'm definitely going to comeback to check out this tutorial again very soon when I'm going to add a feature like this for the front-end interface of my "secret" project.

🌚 Browsing with dark mode makes you a better developer by a factor of exactly 40.

It's a scientific fact.