Next.js
Elevate Your UX: Build a Smooth Custom Cursor with GSAP and React
Create a smooth, animated cursor that glides across the screen, bringing a touch of magic to every movement.
Next.js
Create a smooth, animated cursor that glides across the screen, bringing a touch of magic to every movement.
When it comes to web development, adding unique interactions can significantly elevate the user experience. A custom cursor is a simple yet effective way to achieve that. In this article, we’ll explore how to create a custom cursor using GSAP (GreenSock Animation Platform) and React, focusing on smooth animations and responsiveness.
GSAP is a robust JavaScript library tailored for high-performance animations. Its versatility and ease of use make it a go-to choice for creating smooth, interactive effects. With GSAP, you can achieve frame-perfect animations, even in performance-intensive environments.
To follow along, you’ll need:
txtnpm install gsap @gsap/react
We use useRef to create references for the cursor and its follower. These references enable direct DOM manipulation.
tsxconst cursorRef = useRef<HTMLSpanElement>(null); const followerRef = useRef<HTMLSpanElement>(null);
quickToThe quickTo method efficiently sets element properties, ensuring smooth and high-performance animations. We use it to update the x and y positions of the cursor and follower.
tsx/* Gsap allows the use of either element id, classname or ref */ // using element id (ie, "#cursor", "#follower" const cursorXSetter = gsap.quickTo("#cursor", "x", { duration: 0.2, ease: "power3", }); // using ref const cursorYSetter = gsap.quickTo(cursorRef.current, "y", { duration: 0.2, ease: "power3", });
A mousemove event listener captures the user’s mouse position, which is then used to update the cursor and follower positions.
tsxwindow.addEventListener("mousemove", (e) => { const x = e.clientX; const y = e.clientY; cursorXSetter(x); cursorYSetter(y); followerXSetter(x); followerYSetter(y); });
The cursor and follower are styled spans positioned absolutely. The cursor is a small black dot, while the follower is a larger circle that trails the cursor.
tsx<> <span id="cursor" ref={cursorRef} /> <span id="follower" ref={followerRef} /> </>
Here, the styles are applied using CSS, but you can adapt them to any styling approach. The key points are:
csshtml, body { cursor: none; //required to disable the default cursor } #cursor, #follower { position: fixed; pointer-events: none; z-index: 50; // ensures the pointer is always on top // Styling specific to this example border-radius: 999px; transform: translate(-50%, -50%); } #cursor { height: 0.5rem; width: 0.5rem; background-color: black; } #follower { height: 2rem; width: 2rem; background-color: transparent; border: 1.5px solid black; }
here is the TailwindCSS styling for the same
tsx// Cursor main component <span className="pointer-events-none fixed z-50 h-1 w-1 -translate-x-1/2 -translate-y-1/2 rounded-full bg-black" /> //Cursor follower component <span className="pointer-events-none fixed z-50 h-8 w-8 -translate-x-1/2 -translate-y-1/2 rounded-full border border-black bg-transparent" />
Finally, the CustomCursor component is rendered alongside the main application:
tsxReactDOM.createRoot(document.getElementById("root")!).render( <React.StrictMode> <CustomCursor /> <App /> </React.StrictMode> );
At the core of the custom cursor effect is GSAP’s ability to smoothly transition between values. Here’s how the cursor and follower move:
Cursor Movement:
gsap.quickTo method for both the x and y properties, setting the duration to 0.2 seconds. This ensures the cursor reacts almost instantly to the mouse movement, creating a responsive and snappy feel.Follower Movement:
gsap.quickTo method for the follower has a longer duration of 0.6 seconds. This slower transition causes the follower to lag behind the cursor, giving the visual impression that it’s "following" the cursor's movement.The useGSAP hook is used to integrate GSAP animations within React's component lifecycle in a seamless and efficient way. Here’s why it’s useful:
useRef while ensuring the animations don’t conflict with React's state and re-renders.useGSAP, animations are triggered only when necessary, improving performance and preventing unnecessary re-renders.tsximport { useGSAP } from "@gsap/react"; import gsap from "gsap"; import { useRef } from "react"; const CustomCursor = () => { const cursorRef = useRef<HTMLSpanElement>(null); const followerRef = useRef<HTMLSpanElement>(null); useGSAP(() => { const cursorXSetter = gsap.quickTo("#cursor", "x", { duration: 0.2, ease: "power3", }); const cursorYSetter = gsap.quickTo(cursorRef.current, "y", { duration: 0.2, ease: "power3", }); const followerXSetter = gsap.quickTo(followerRef.current, "x", { duration: 0.6, ease: "power3", }); const followerYSetter = gsap.quickTo("#follower", "y", { duration: 0.6, ease: "power3", }); window.addEventListener("mousemove", (e) => { const x = e.clientX; const y = e.clientY; cursorXSetter(x); cursorYSetter(y); followerXSetter(x); followerYSetter(y); }); }, []); return ( <> <span id="cursor" ref={cursorRef} /> <span id="follower" ref={followerRef} /> </> ); }; export default CustomCursor;
For a live demonstration and editable code, check out this
https://codesandbox.io/p/sandbox/custom-cursor-m283xv
This custom cursor effect is highly customizable. Here are some ideas for further enhancement:
Happy coding….