Magnet
Wrapper component to add a magnetic effect to your elements
Motion
Tailwind CSS
React
Next.js
Demo
Hover me
High effect
Magnet: Inactive
"use client";
import Magnet from "./magnet";
import { StarIcon } from "lucide-react";
import { useState } from "react";
const MagnetDemo: React.FC = () => {
const [isActive, setIsActive] = useState(false);
return (
<div className="flex flex-col md:flex-row w-full items-center justify-around gap-20 md:gap-0">
<Magnet>
<div>Hover me</div>
</Magnet>
<Magnet className="rounded-full bg-accent cursor-pointer text-accent-foreground p-3 border border-accent-foreground/10">
<StarIcon className="size-6" />
</Magnet>
<Magnet magnetMultiplier={3}>
High effect
</Magnet>
<div className="flex flex-col gap-4 justify-center items-center">
<Magnet isActive={isActive}>
Magnet: {isActive ? "Active" : "Inactive"}
</Magnet>
<button onClick={() => setIsActive(!isActive)} className="text-xs">
Toggle
</button>
</div>
</div>
);
}
export { MagnetDemo };Installation
Install the following dependencies:
npm install motionpnpm add motionyarn add motionbun add motionCopy and paste the following code into your project.
"use client";
import { useEffect, useRef, useState } from "react";
import { motion, useSpring } from "motion/react";
import { cn } from "@/lib/utils";
interface MagnetProps {
children: React.ReactNode;
className?: string;
/**
* To programmatically activate/deactivate the magnetic effect
*/
isActive?: boolean;
/**
* Default is 1. It multiplies the force of the magnetic effect.
*/
magnetMultiplier?: number;
}
const Magnet: React.FC<MagnetProps> = ({
children,
className,
isActive = true,
magnetMultiplier = 1,
}) => {
const ref = useRef<HTMLDivElement>(null);
const [position, setPosition] = useState({ x: 0, y: 0 });
const mouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!isActive || !ref.current) return;
const { clientX, clientY } = e;
const { width, height, left, top } = ref.current.getBoundingClientRect();
const x =
(clientX - (left + width / 2)) *
(magnetMultiplier < 0 ? 1 : (magnetMultiplier < 0 ? 1 : magnetMultiplier));
const y =
(clientY - (top + height / 2)) *
(magnetMultiplier < 0 ? 1 : (magnetMultiplier < 0 ? 1 : magnetMultiplier));
setPosition({ x, y });
};
const mouseLeave = () => {
setPosition({ x: 0, y: 0 });
};
const x = useSpring(position.x, { stiffness: 400, damping: 60, mass: 0.2 });
const y = useSpring(position.y, { stiffness: 400, damping: 60, mass: 0.2 });
useEffect(() => {
x.set(position.x);
y.set(position.y);
}, [position]);
useEffect(() => {
if (!isActive) {
setPosition({ x: 0, y: 0 });
}
}, [isActive]);
return (
<motion.div
className={cn('relative', className)}
ref={ref}
whileHover={isActive ? { scale: 1.1 } : {}}
transition={{ type: 'spring', stiffness: 300 }}
onMouseMove={mouseMove}
onMouseLeave={mouseLeave}
style={{ x, y }}
>
{children}
</motion.div>
);
};
export default Magnet;Usage
import Magnet from "@/components/magnet";// Default usage
<Magnet>
<div>Hover me</div>
</Magnet>// Custom magnet effect
<Magnet magnetMultiplier={1.2}>
<div>Hover me</div>
</Magnet>Props
| Prop | Type | Default Value | Description |
|---|---|---|---|
| children | ReactNode | - | The content of the magnet |
| className? | string | - | Additional classes for the magnet |
| magnetMultiplier? | number | 1 | The multiplier for the magnetic effect |
| isActive? | boolean | true | Whether the magnetic effect is active |