Trailing Cursor

A customizable trailing cursor effect that follows the mouse position with a smooth transition.

Trailing Cursor
Cursor Effect
Cursor Follow

KAIALAN RAZZ

APR 2024

Note

Adjust the posX and posY values to customize the cursor's trailing effect relative to the mouse position. Default values are set to 150, but feel free to modify them by passing them as parameters to the component.

Installation

1

Install dependencies

npm i framer-motion clsx tailwind-merge
2

Add util file

Add this code to your util file.

import { ClassValue, clsx } from "clsx";
  import { twMerge } from "tailwind-merge";
  
  export function cn(...inputs: ClassValue[]) {
    return twMerge(clsx(inputs));
  }
  
3

Add the hook.

Add this useMousePosition hook file.

hooks/useMousePosition.tsx
'use client'

import { useState, useEffect } from "react";

const useMousePosition = () => {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

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

  useEffect(() => {
    window.addEventListener("mousemove", updateMousePosition);

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

  return mousePosition;
};

export default useMousePosition;
4

Copy the source code

components/ui/wavy-line.tsx
import { motion } from "framer-motion";
import useMousePosition from "@/hooks/useMousePosition";
import { cn } from "@/lib/utils";

const TrailingCursor = ({
  posX,
  posY,
  duration,
  className,
}: {
  posX?: number;
  posY?: number;
  duration?: number;
  className?: string;
}) => {
  posX = posX || 150;
  posY = posY || 150;
  duration = duration || 3;
  const { x, y } = useMousePosition();
  return (
    <motion.div
      className={cn(
        "h-[300px] w-[300px] rounded-full bg-gradient-to-t from-[#92209D]  via-[#D84549] to-[#D34740] blur-[50px] fixed top-0 left-0",
        className
      )}
      animate={{
        x: x - posX,
        y: y - posY,
      }}
      transition={{ type: "tween", ease: "backOut", duration: duration }}
    ></motion.div>
  );
};

export default TrailingCursor;