Getting Started
Accordion
Barricade Tape
Buttons
Browser Window
Contact Section
File Stack
Glass Grid
NewGooey Words
Glowing Dots Background
Image Pile
Jelly Loader
Mask Cursor Effect
NewMagnet Tabs
Masonry Grid
Pixelated Carousel
NewPixelated Text
NewPrismatic Haze Background
Projects Section
Proximity Background
Proximity Lift Grid
Skeumorphic Music Card
Spotlight Grid
Texts
Trading Card
Wavy Background
NewMask Cursor Effect
A dynamic and interactive React component that creates an engaging cursor-following mask effect using Framer Motion animations. This TypeScript component reveals hidden content through a smooth circular mask that tracks mouse movement, expanding on hover to create stunning reveal animations. Perfect for modern web applications seeking to enhance user engagement with creative UI interactions, spotlight effects, or content reveals. Built with performance in mind using CSS masks and optimized animations.
Meet the Designer
Preview
Follow below steps 👇🏻
Install dependencies
npm i framer-motion
Component
Create a file mask-cursor-effect.tsx in your components folder and paste this code
'use client';
import React, { useEffect, useRef, useState } from 'react';
import { motion } from 'framer-motion';
import { cn } from '@/lib/utils';
const getMaskDataUrl = () => {
const svgString = `<svg width="526" height="526" viewBox="0 0 526 526" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="263" cy="263" r="263" fill="black" />
</svg>`;
return `data:image/svg+xml;base64,${btoa(svgString)}`;
};
const MaskCursorEffect = ({
children,
hiddenComponent,
className,
compressedMaskSize = 40,
expandedMaskSize = 350
}: {
children: React.ReactNode;
hiddenComponent?: React.ReactNode;
className?: string;
compressedMaskSize?: number;
expandedMaskSize?: number;
}) => {
const [mousePosition, setMousePosition] = useState({ x: 20, y: 20 });
const [isHovered, setIsHovered] = useState(false);
const wrapperRef = useRef<HTMLDivElement>(null);
const MASK_SIZE = isHovered ? expandedMaskSize : compressedMaskSize;
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
const { left, top } = wrapperRef.current?.getBoundingClientRect() || {
left: 0,
top: 0
};
setMousePosition({ x: e.clientX - left, y: e.clientY - top });
};
wrapperRef.current?.addEventListener('mousemove', handleMouseMove);
return () => wrapperRef.current?.removeEventListener('mousemove', handleMouseMove);
}, [wrapperRef]);
return (
<div ref={wrapperRef} className="h-full w-full relative flex flex-col">
<motion.div
animate={{
WebkitMaskPosition: `${mousePosition.x - MASK_SIZE / 2}px ${
mousePosition.y - MASK_SIZE / 2
}px`,
maskSize: `${MASK_SIZE}px ${MASK_SIZE}px`
}}
style={{
maskImage: `url("${getMaskDataUrl()}")`,
WebkitMaskImage: `url("${getMaskDataUrl()}")`,
maskRepeat: 'no-repeat',
WebkitMaskRepeat: 'no-repeat',
backgroundColor: '#EA5A47',
color: 'black'
}}
transition={{
type: 'tween',
ease: 'backOut'
}}
className={cn(
'h-[800px] w-full flex items-center justify-center absolute z-10 ',
className
)}
>
<div onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
{hiddenComponent}
</div>
</motion.div>
<div
className={cn('h-[800px] w-full flex items-center justify-center text-white/70', className)}
>
{children}
</div>
</div>
);
};
export default MaskCursorEffect;
Usage
<div className="h-full w-full flex flex-col gap-5 items-center justify-center overflow-y-auto relative p-10">
<MaskCursorEffect
hiddenComponent={
<div className="max-w-4xl text-7xl font-bold">
I'm a "full-stack developer" powered by Stack Overflow and prayer. My code is 10%
genius, 90% duct tape.
</div>
}
>
<div className="max-w-4xl text-7xl font-bold">
I'm a software engineer specializing in React, Next.js, and TypeScript. I build
creative interfaces with solid backend logic.
</div>
</MaskCursorEffect>
</div>