Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Buttons

Free

Magnetic Button for React

A button that leans toward the pointer while it's over it and springs back when it leaves. For React with Base UI, when a hero call to action should invite the click.

Framework
UI library

As the pointer moves over the button, the button leans toward it, following it across its face. When the pointer leaves, it springs back into place with a small overshoot.

Use it for one call to action on a landing page or a hero, where a little motion earns a second look. Don't use it in forms, toolbars or dense layouts, where a moving target is harder to hit; for a quieter answer to the pointer, use the Spotlight Button. It wraps the Button, so variant and size work as usual. Each pointer move writes the offset into two CSS variables that translate the button, and a CSS transition makes the release springy, so nothing re-renders while it moves. It stays still on touch screens, while disabled, and when the system asks for reduced motion.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Use email insteadNo credit card

Usage

import { MagneticButton } from "@/components/ui/magnetic-button";
<MagneticButton>Get started</MagneticButton>

Demos

Intensity

intensity is how strongly the button follows the pointer: subtle travels a sixth of the pointer's distance from its center, medium (the default) a third and strong a half. Keep strong for a large button with room around it, since it slides well out of its place.

Preview width

Accessibility

The movement is visual only: the button is announced and activated as any Button, and focus, Enter and Space work as usual. The pull follows a mouse or a pen, never a touch, and it's off when the system asks for reduced motion, so the button stays where it was laid out.

Enter
Activate the button.
Space
Activate the button.

FAQ

How does the spring work without an animation library?

While the pointer is over the button, it follows with a short ease-out transition. When the pointer leaves, the offset goes back to zero over half a second with a cubic-bezier that overshoots, which reads as a spring. A data-pulled attribute on the button, set while it follows, switches between the two timings.

The pull feels too floaty. How do I tone it down?

Set intensity="subtle", so the button travels a sixth of the pointer's distance from its center instead of a third.

Does it start moving before the pointer is over it?

No. It follows the pointer only while the pointer is over the button, so it never moves toward a pointer that's just passing by, and it costs nothing while the pointer is elsewhere on the page.

Does the moving button push its neighbors?

No. The pull is a CSS translate, which moves the button without changing the layout, so nothing around it shifts. Leave some room around it so it doesn't slide over its neighbors.

What happens on a touch screen?

Nothing moves. The pull only follows a mouse or a pen, because a finger has no hover and the button would jump under it. A tap presses it as usual.

Can I make it full width?

Yes. Pass className="w-full": there's no wrapper, so flex or grid layouts place the button directly. A wide button moves just as far, so subtle often suits it best.

What happens with reduced motion?

The button stays in place. It still presses, focuses and activates as usual.

API Reference

MagneticButton

PropType
focusableWhenDisabled?boolean

Whether the button should be focusable when disabled.

Default
false
intensity?"subtle" | "medium" | "strong"

How strongly the button follows the pointer: subtle travels a sixth of the pointer's distance from its center, medium a third and strong a half.

Type
"subtle" | "medium" | "strong"
Default
"medium"
nativeButton?boolean

Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).

Default
true
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

CSS variableType
--magnetic-button-xstring

How far the button is pulled across, in pixels.

--magnetic-button-ystring

How far the button is pulled down, in pixels.

All 35 buttons