Sora UI

Magnetic Cards

A fanned stack of cards that spring away from the cursor with GSAP-driven, velocity-based physics.

Made by Axyl

Move your cursor across the fanned stack — cards spring away based on cursor speed and proximity.

Loading...

MagneticCards runs its spring simulation on GSAP's ticker (gsap.ticker), driving each card with gsap.set every frame instead of React state — the same low-overhead approach used by ScrollGallery and TextRevealBox.

Installation

File Structure

magnetic-cards.tsx
use-prefers-reduced-motion.tsx

Usage

import { MagneticCards } from '@/components/sora-ui/effects/magnetic-cards';

const items = [
  { src: '/cards/card-1.jpg', alt: 'Card one' },
  { src: '/cards/card-2.jpg', alt: 'Card two' },
  { src: '/cards/card-3.jpg', alt: 'Card three' },
  { src: '/cards/card-4.jpg', alt: 'Card four' },
];

export default function Page() {
  return (
    <div className="h-[420px] w-full">
      <MagneticCards items={items} />
    </div>
  );
}

Props

PropTypeDefault
items?
MagneticCardsItem[]
-
layout?
MagneticCardsLayout[]
-
proximityRadius?
number
500
pushForce?
number
10
tiltAmount?
number
0.1
neighborInfluence?
number
0.2
springStiffness?
number
0.05
bounceFriction?
number
0.85
cursorSmoothing?
number
0.75
cardClassName?
string
-
className?
string
-

MagneticCardsItem

PropTypeDefault
src?
string
-
alt?
string
-

When to Use

Use MagneticCards for hero sections, portfolio spotlights, or landing pages where a small set of images (3-6 works best) deserves a tactile, playful entrance. The physics simulation reads best with generous surrounding space — give the root element real height (e.g. a fixed-height section) rather than letting it collapse to content size.

For image grids that need to stay readable and scannable (e.g. a gallery or catalog), use a static grid instead — the constant motion here is decorative, not information-dense.

Accessibility

Cursor tracking is scoped to the component's own root element via pointermove/pointerleave, so it never hijacks page-wide input. When prefers-reduced-motion: reduce is set, the physics loop never starts — cards render directly at their rest position/rotation with no motion at all, and the setting is watched live via usePrefersReducedMotion, so toggling it while the page is open updates immediately.

Credits

Inspired by Spencer Gabor. Reimplemented for GSAP and React.

Built by Axyl. A motion-first component registry for React.

Last updated: 7/1/2026