Following Pointer
A custom pointer that follows mouse arrow and animates in pointer and content.

Amazing Tailwindcss Grid Layout Examples
Grids are cool, but Tailwindcss grids are cooler. In this article, we will learn how to create amazing Grid layouts with Tailwindcs grid and React.
28th March, 2023
Read More
Installation
Install dependencies
npm i motion clsx tailwind-merge
Add util file
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Copy the source code
components/ui/following-pointer.tsx
// Core component that receives mouse positions and renders pointer and content
import React, { useEffect, useState } from "react";
import { motion, AnimatePresence, useMotionValue } from "motion/react";
import { cn } from "@/lib/utils";
export const FollowerPointerCard = ({
children,
className,
title,
}: {
children: React.ReactNode;
className?: string;
title?: string | React.ReactNode;
}) => {
const x = useMotionValue(0);
const y = useMotionValue(0);
const ref = React.useRef<HTMLDivElement>(null);
const [rect, setRect] = useState<DOMRect | null>(null);
const [isInside, setIsInside] = useState<boolean>(false); // Add this line
useEffect(() => {
if (ref.current) {
setRect(ref.current.getBoundingClientRect());
}
}, []);
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (rect) {
const scrollX = window.scrollX;
const scrollY = window.scrollY;
x.set(e.clientX - rect.left + scrollX);
y.set(e.clientY - rect.top + scrollY);
}
};
const handleMouseLeave = () => {
setIsInside(false);
};
const handleMouseEnter = () => {
setIsInside(true);
};
return (
<div
onMouseLeave={handleMouseLeave}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove}
style={{
cursor: "none",
}}
ref={ref}
className={cn("relative", className)}
>
<AnimatePresence>
{isInside && <FollowPointer x={x} y={y} title={title} />}
</AnimatePresence>
{children}
</div>
);
};
export const FollowPointer = ({
x,
y,
title,
}: {
x: any;
y: any;
title?: string | React.ReactNode;
}) => {
const colors = [
"#0ea5e9",
"#737373",
"#14b8a6",
"#22c55e",
"#3b82f6",
"#ef4444",
"#eab308",
];
return (
<motion.div
className="absolute z-50 h-4 w-4 rounded-full"
style={{
top: y,
left: x,
pointerEvents: "none",
}}
initial={{
scale: 1,
opacity: 1,
}}
animate={{
scale: 1,
opacity: 1,
}}
exit={{
scale: 0,
opacity: 0,
}}
>
<svg
stroke="currentColor"
fill="currentColor"
strokeWidth="1"
viewBox="0 0 16 16"
className="h-6 w-6 -translate-x-[12px] -translate-y-[10px] -rotate-[70deg] transform stroke-sky-600 text-sky-500"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M14.082 2.182a.5.5 0 0 1 .103.557L8.528 15.467a.5.5 0 0 1-.917-.007L5.57 10.694.803 8.652a.5.5 0 0 1-.006-.916l12.728-5.657a.5.5 0 0 1 .556.103z"></path>
</svg>
<motion.div
style={{
backgroundColor: colors[Math.floor(Math.random() * colors.length)],
}}
initial={{
scale: 0.5,
opacity: 0,
}}
animate={{
scale: 1,
opacity: 1,
}}
exit={{
scale: 0.5,
opacity: 0,
}}
className={
"min-w-max rounded-full bg-neutral-200 px-2 py-2 text-xs whitespace-nowrap text-white"
}
>
{title || `William Shakespeare`}
</motion.div>
</motion.div>
);
};
Props
FollowerPointerCard Props
Prop name | Type | Description |
---|---|---|
children | React.ReactNode | The content to be rendered inside the component. |
className | string (optional) | The class name of the FollowerPointerCard component. |
title | string or ReactNode (optional) | The title to be displayed on the pointer. |
Build websites faster and 10x better than your competitors with Aceternity UI Pro
With the best in class components and templates, stand out from the crowd and get more attention to your website. Trusted by founders and entrepreneurs from all over the world.
Manu was quick to respond, very professional, and delivered a website within a week. Very good job. Looking forward to collaborating again
Asriel Han
Founder, CTO at Advex AI