Hello Guys, In this quick tutorial, we'll explore how to create a custom cursor for your Next.js application. Our requirement for this custom cursor was to follow our main cursor and dynamically change its color based on the type of element it hovers over. Using this foundation knowledge you can build your own cursor matching your imagination or requirements.
Before we start, ensure you have a Next.js project set up.
npx create-next-app@latest
Open your project in your code editor and create a new component for the custom cursor under the components folder if you selected to create src
folder while creating your next js project then the components folder should be under src else place it on your project root. Create a file named CustomCursor.tsx
in the components
folder and add the following code:
//src/components/custom-cursor.tsx
// Import necessary React hooks and components
import React, {useEffect, useRef, useState } from 'react';
// Define cursor colors
const CURSOR_COLORS = {
"h1": "green-400",
"button": "orange-500",
"default": "sky-500"
};
// Main CustomCursor component
const CustomCursor = () => {
// Reference to the cursor element
const cursorRef = useRef(null);
// State to track cursor position
const [position, setPosition] = useState({ x: 0, y: 0 });
// State to track cursor color
const [cursorColor, setCursorColor] = useState("sky-500");
// State to track click event
const [clicked, setClicked] = useState(false);
useEffect(() => {
// Event listener for mouse movement
const handleMouseMove = (e) => {
setPosition({
x: e.clientX
y: e.clientY
});
};
// Event listener for mouse click
const handleMouseDown = () => {
setClicked(true);
// Reset click state after 800 milliseconds
setTimeout(() => {
setClicked(false);
}, 800);
};
// Event listener for mouseover (hover) on HTML elements
const handleMouseOver = (e) => {
// Get the HTML tag name
const tagName = e.target.tagName.toLowerCase();
// Set cursor color based on the tag, default to "sky-500"
setCursorColor(CURSOR_COLORS[tagName] || CURSOR_COLORS["default"]);
};
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mousedown", handleMouseDown);
window.addEventListener("mouseover", handleMouseOver);
// Cleanup event listeners on component unmount
return () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mousedown", handleMouseDown);
window.removeEventListener("mouseover", handleMouseOver);
};
}, []); // useEffect runs only once on mount
return (
<>
<div
style={{ top: position.y, left: position.x }}
ref={cursorRef}
className={`fixed pointer-events-none transition-all -translate-x-1/2 -translate-y-1/2 z-50 ease-in duration-300 rounded-full w-3 h-3 bg-${cursorColor}`}
/>
<div
style={{ top: position.y, left: position.x }}
ref={cursorRef}
className={`p-0 fixed pointer-events-none transition-all -translate-x-1/2 -translate-y-1/2 z-50 ease-in duration-500 rounded-full w-8 h-8 border-2 border-${cursorColor} `}
>
<div
className={`w-8 h-8 ${clicked ? "scale-100 opacity-30" : "scale-0 opacity-0"} -translate-x-[1px] -translate-y-[1px] rounded-full bg-${cursorColor} ease-in transition-all duration-500 -z-10`}
/>
</div>
</>
);
};
export default CustomCursor;
Let's do the code breakdown.
Starting with imports we need the react hooksuseState
to manage state variables for cursor position, color, and click status. useEffect
Handle side effects like setting up event listeners for mouse events.useRef
Provides a reference to the cursor element for direct DOM interaction. The CURSOR_COLORS
object is defined to map HTML tag names to specific cursor colors, allowing for a dynamic and visually appealing cursor based on the hovered HTML elements, for now, we have just defined elements you can create for other elements as well.
We have 3 state variables position
to track the current cursor position, cursorColor
to track the current cursor color and clicked
to track the click event for a click effect.
Inside the useEffect
hook, we have handleMouseMove
function which updates the position
state based on the mouse coordinates. The handleMouseDown
function triggers a click effect by setting the clicked
state to true
and resetting it after a short delay of 800ms and handleMouseOver
, responsible for dynamically changing the cursor color based on the HTML tag being hovered. It checks the tag name using the e.target.tagName
property and assigns a corresponding color from the CURSOR_COLORS
object. The useEffect
hook also ensures the cleanup of these event listeners when the component unmounts.
Finally, for the JSX, we have 3 divs one div for solid circle, one for outline, and one for showing click effect each styled using tailwindcss. Important styles to note here are below:
fixed
: Positions the cursor elements relative to the viewport, making them fixed in place during scrolling.pointer-events-none
: Ensures that the cursor elements do not block or interfere with mouse events, allowing underlying elements to receive them.-translate-x-1/2 -translate-y-1/2
: Translates the cursor elements horizontally and vertically by half of their own width and height, respectively, to center them on the mouse position.transition, duration-, ease-in
: Used for smoothing the cursor movements and changes.If you want to know more details on styles you can refer to Tailwindcss docs
Now, let's integrate the CustomCursor
component into your Next.js application. Open the layout.tsx file in the app folder and import the CustomCursor
component and place it below the body tag:
// app/layouts.tsx
import type { Metadata } from 'next'
import { Karla } from 'next/font/google'
import './globals.css'
import CustomCursor from '@/components/custom-cursor'
const karla = Karla({ subsets: ['latin'], variable: "--font-karla" })
export const metadata: Metadata = {
title: 'Custom Cursor',
description: '',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body suppressHydrationWarning={true} className={karla.className}>
<CustomCursor />{children}</body>
</html>
)
}
This modification ensures that the CustomCursor
component is rendered on every page of your Next.js application. You can now add h1 tags, buttons, etc.
Start your Next.js application to see the custom cursor in action:
npm run dev
Visit http://localhost:3000
in your browser and hover over different elements to observe the custom cursor adapting its appearance.
Congratulations! You've successfully implemented a custom cursor in your Next.js application. Feel free to customize the styles and behavior further based on your project's requirements.