buttons
Magnetic Button
A button that magnetically follows the cursor on hover using spring physics.
buttonhoveranimationmagneticinteractive
▶Preview
🖲Usage
example-usage.tsx
import { MagneticButton } from "@/components/kanso/magnetic-button"
export default function MagneticButtonDemo() {
return (
<div className="flex gap-4">
<MagneticButton>Hover Me</MagneticButton>
<MagneticButton variant="outline" magneticStrength={0.4}>
Custom Strength
</MagneticButton>
</div>
)
}↓Installation
1
Create folder & copy source
Create a folder named kanso inside your project's components directory (i.e. components/kanso/). Copy the source code shown in the next section, and paste it into a file named magnetic-button.tsx inside it.
2
Install dependencies
pnpm add framer-motion3
Required helper files
Ensure your project has the following helper files configured:
- →
lib/utils
<>Source Code
magnetic-button.tsx
'use client';
import * as React from 'react';
import {
motion,
useMotionValue,
useSpring,
type HTMLMotionProps,
} from 'framer-motion';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const magneticButtonVariants = cva(
'relative inline-flex items-center justify-center rounded-lg text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default:
'border border-zinc-200 bg-zinc-900 px-6 py-2.5 text-white hover:bg-zinc-800 dark:border-zinc-700 dark:bg-white dark:text-zinc-900 dark:hover:bg-zinc-100',
outline:
'border border-zinc-200 bg-transparent px-6 py-2.5 text-zinc-900 hover:bg-zinc-100/60 dark:border-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-900/60',
},
},
defaultVariants: {
variant: 'default',
},
}
);
interface MagneticButtonProps
extends
Omit<HTMLMotionProps<'button'>, 'ref'>,
VariantProps<typeof magneticButtonVariants> {
/** Strength of the magnetic pull effect (default: 0.3) */
magneticStrength?: number;
}
function MagneticButton({
children,
className,
variant,
magneticStrength = 0.3,
disabled,
onClick,
...props
}: MagneticButtonProps) {
const ref = React.useRef<HTMLButtonElement>(null);
const x = useMotionValue(0);
const y = useMotionValue(0);
const springX = useSpring(x, { stiffness: 150, damping: 15, mass: 0.1 });
const springY = useSpring(y, { stiffness: 150, damping: 15, mass: 0.1 });
const handleMouseMove = React.useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
if (!ref.current) return;
const rect = ref.current.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
x.set((e.clientX - centerX) * magneticStrength);
y.set((e.clientY - centerY) * magneticStrength);
},
[magneticStrength, x, y]
);
const handleMouseLeave = React.useCallback(() => {
x.set(0);
y.set(0);
}, [x, y]);
return (
<motion.button
ref={ref}
style={{ x: springX, y: springY }}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
onClick={onClick}
disabled={disabled}
className={cn(magneticButtonVariants({ variant, className }))}
{...props}
>
{children}
</motion.button>
);
}
export { MagneticButton };
export type { MagneticButtonProps };