magnetic-button

PreviousNext

A Button component with magnetic effect animation on hover.

Docs
tailwind-admincomponent

Preview

Loading preview…
app/components/animatedComponents/buttons/magnetic/MagneticButton.tsx
'use client'

import * as React from 'react'

import { motion, type HTMLMotionProps, type Transition } from 'motion/react'
import type { VariantProps } from 'class-variance-authority'

import { cn } from '@/lib/utils'
import { buttonVariants } from '@/components/ui/button'

interface Position {
  x: number
  y: number
}

interface MagneticButtonProps extends HTMLMotionProps<'button'>, VariantProps<typeof buttonVariants> {
  scale?: number
  transition?: Transition
}

function MagneticButton({ className, size, variant, ...props }: MagneticButtonProps) {
  const ref = React.useRef<HTMLButtonElement>(null)
  const [position, setPosition] = React.useState<Position>({ x: 0, y: 0 })

  const handleMouse = (e: React.MouseEvent<HTMLButtonElement>) => {
    if (ref.current) {
      const { clientX, clientY } = e
      const { height, width, left, top } = ref.current.getBoundingClientRect()
      const middleX = clientX - (left + width / 2)
      const middleY = clientY - (top + height / 2)

      setPosition({ x: middleX, y: middleY })
    }
  }

  const reset = () => {
    setPosition({ x: 0, y: 0 })
  }

  const { x, y } = position

  return (
    <motion.button
      ref={ref}
      onMouseMove={handleMouse}
      onMouseLeave={reset}
      animate={{ x, y }}
      transition={{ type: 'spring', stiffness: 170, damping: 18, mass: 0.1 }}
      whileTap={{
        scale: 0.75
      }}
      className={cn(buttonVariants({ variant, size }), 'relative transition-none', className)}
      {...props}
    >
      Magnetic Button
    </motion.button>
  )
}

export { MagneticButton, type MagneticButtonProps }

Installation

npx shadcn@latest add @tailwind-admin/magnetic-button

Usage

import { MagneticButton } from "@/components/magnetic-button"
<MagneticButton />