Docs
Magnetic Card

Magnetic Card

An interactive card component with magnetic attraction, 3D rotation, and visual effects

Glass Effect

🎨

Outline Style

🚀

Gradient Style

Hover to explore

💫

Interactive

Magnetic3D

Installation

Copy and paste the following code into your project.

components/ui/magnetic-card.tsx

"use client";
 
import React, { useRef, useEffect, useState } from "react";
import { cn } from "@/lib/utils";
 
export interface MagneticCardProps
  extends React.HTMLAttributes<HTMLDivElement> {
  children: React.ReactNode;
  magneticStrength?: number;
  rotationStrength?: number;
  scaleOnHover?: number;
  showMagneticField?: boolean;
  fieldColor?: string;
  darkFieldColor?: string;
}
 
export function MagneticCard({
  children,
  className,
  magneticStrength = 0.5,
  rotationStrength = 15,
  scaleOnHover = 1.1,
  showMagneticField = false,
  fieldColor = "rgba(147, 51, 234, 0.15)",
  darkFieldColor = "rgba(168, 85, 247, 0.25)",
  ...props
}: MagneticCardProps) {
  const cardRef = useRef<HTMLDivElement>(null);
  const [isHovered, setIsHovered] = useState(false);
  const fieldRef = useRef<HTMLDivElement>(null);
 
  useEffect(() => {
    const card = cardRef.current;
    const field = fieldRef.current;
    if (!card) return;
 
    const handleMouseMove = (e: MouseEvent) => {
      const { left, top, width, height } = card.getBoundingClientRect();
      const centerX = left + width / 2;
      const centerY = top + height / 2;
      const mouseX = e.clientX;
      const mouseY = e.clientY;
 
      // Calculate distance from mouse to card center
      const deltaX = mouseX - centerX;
      const deltaY = mouseY - centerY;
      const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
 
      // Calculate magnetic pull (stronger when closer)
      const maxDistance = Math.max(window.innerWidth, window.innerHeight) / 2;
      const pull = Math.max(0, 1 - distance / maxDistance);
 
      // Calculate rotation based on mouse position
      const rotateX = (deltaY / height) * rotationStrength;
      const rotateY = -(deltaX / width) * rotationStrength;
 
      // Apply transform with easing
      const moveX = deltaX * pull * magneticStrength;
      const moveY = deltaY * pull * magneticStrength;
 
      const transform = `
        translate(${moveX}px, ${moveY}px)
        rotateX(${rotateX}deg)
        rotateY(${rotateY}deg)
        scale(${isHovered ? scaleOnHover : 1})
      `;
 
      card.style.transform = transform;
      card.style.transition = "transform 0.3s cubic-bezier(0.23, 1, 0.32, 1)";
 
      // Update magnetic field effect if enabled
      if (showMagneticField && field) {
        const fieldScale = 1 + pull * 0.5;
        field.style.transform = `scale(${fieldScale})`;
        field.style.opacity = (pull * 0.5).toString();
      }
    };
 
    const handleMouseEnter = () => {
      setIsHovered(true);
      if (showMagneticField && field) {
        field.style.opacity = "0.3";
      }
    };
 
    const handleMouseLeave = () => {
      setIsHovered(false);
      card.style.transform = "translate(0, 0) rotateX(0) rotateY(0) scale(1)";
      if (showMagneticField && field) {
        field.style.transform = "scale(1)";
        field.style.opacity = "0";
      }
    };
 
    document.addEventListener("mousemove", handleMouseMove);
    card.addEventListener("mouseenter", handleMouseEnter);
    card.addEventListener("mouseleave", handleMouseLeave);
 
    return () => {
      document.removeEventListener("mousemove", handleMouseMove);
      card.removeEventListener("mouseenter", handleMouseEnter);
      card.removeEventListener("mouseleave", handleMouseLeave);
    };
  }, [
    magneticStrength,
    rotationStrength,
    scaleOnHover,
    isHovered,
    showMagneticField,
  ]);
 
  return (
    <div className="relative" style={{ perspective: "1000px" }}>
      {showMagneticField && (
        <div
          ref={fieldRef}
          className="absolute inset-0 -z-10 rounded-[inherit] [--dark-field-color:var(--_dark-field-color)] dark:[--mode-field-color:var(--dark-field-color)]"
          style={
            {
              background: `radial-gradient(circle at center, var(--field-color) 0%, transparent 70%)`,
              transform: "scale(1.5)",
              opacity: 0,
              transition: "transform 0.3s ease, opacity 0.3s ease",
              "--field-color": `var(--mode-field-color, ${fieldColor})`,
              "--_dark-field-color": darkFieldColor,
            } as React.CSSProperties
          }
        />
      )}
      <div
        ref={cardRef}
        className={cn(
          "relative rounded-lg bg-card text-card-foreground shadow-xs transition-all will-change-transform dark:shadow-lg dark:shadow-purple-500/5",
          className,
        )}
        {...props}
      >
        {children}
      </div>
    </div>
  );
}

Update the import paths to match your project setup.

import MagneticCard from "@/components/ui/magnetic-card";

Usage

import MagneticCard from "@/components/ui/magnetic-card";
 
export default function Example() {
  return (
    <MagneticCard
      showMagneticField={true}
      magneticStrength={0.4}
      className="p-6"
    >
      <p>Card content</p>
    </MagneticCard>
  );
}

Props

PropTypeDescriptionDefault
magneticStrengthnumberControls the strength of the magnetic effect (0-1)0.5
rotationStrengthnumberControls the degree of 3D rotation (0-45)15
scaleOnHovernumberScale factor when hovering over the card1.1
showMagneticFieldbooleanShows a visual magnetic field effectfalse
fieldColorstringColor of the magnetic field in light modergba(147, 51, 234, 0.15)
darkFieldColorstringColor of the magnetic field in dark modergba(168, 85, 247, 0.25)
childrenReactNodeThe content to be rendered inside the card-
classNamestringAdditional CSS classes to apply to the card-

Examples

Glass Effect Card

<MagneticCard
  className="p-6 bg-white/80 dark:bg-white/10 backdrop-blur-xl border border-purple-200 dark:border-white/20 shadow-xl"
  showMagneticField={true}
  magneticStrength={0.4}
>
  <p className="text-lg font-medium bg-clip-text text-transparent bg-linear-to-r from-purple-500 to-pink-500">
    Glass Effect
  </p>
</MagneticCard>

Outline Style Card

<MagneticCard
  className="p-6 border-2 border-purple-500/30 dark:border-purple-400/30 bg-white/50 dark:bg-background/50"
  showMagneticField={true}
  magneticStrength={0.3}
  rotationStrength={20}
>
  <p className="text-lg font-medium text-purple-950 dark:text-purple-100">
    Outline Style
  </p>
</MagneticCard>

Gradient Style Card

<MagneticCard
  className="p-6 bg-linear-to-br from-purple-500 to-pink-500 dark:from-purple-600 dark:to-pink-600 text-white"
  showMagneticField={true}
  fieldColor="rgba(236, 72, 153, 0.15)"
  darkFieldColor="rgba(236, 72, 153, 0.25)"
  scaleOnHover={1.15}
>
  <p className="text-lg font-medium">Gradient Style</p>
</MagneticCard>

Interactive Card with Custom Content

<MagneticCard
  className="overflow-hidden group bg-white dark:bg-card"
  showMagneticField={true}
  magneticStrength={0.5}
  rotationStrength={12}
>
  <div className="p-6 flex flex-col items-center gap-2">
    <div className="text-2xl group-hover:scale-110 duration-300">💫</div>
    <p className="text-lg font-medium text-purple-950 dark:text-purple-100">
      Interactive
    </p>
    <div className="flex gap-2">
      <span className="px-2 py-0.5 rounded-full text-xs bg-purple-100 dark:bg-purple-400/20 text-purple-700 dark:text-purple-300">
        Magnetic
      </span>
    </div>
  </div>
</MagneticCard>