Something new is coming.Join the waitlist

Dock

PreviousNext

macOS-style React Dock with magnetic hover. Icons grow as the cursor approaches, driven by Framer Motion spring transforms. Accessible buttons under the hood.

Installation

npx shadcn@latest add https://tentui.com/r/dock.json

Usage

Wrap a row of DockIcons in Dock. Each icon reads the shared cursor position from context and animates its own scale.

import { Home, Mail, Settings } from "lucide-react";
 
import { Dock, DockIcon } from "@/components/dock";
 
export function AppDock() {
  return (
    <Dock>
      <DockIcon aria-label="Home">
        <Home className="size-1/2" />
      </DockIcon>
      <DockIcon aria-label="Mail">
        <Mail className="size-1/2" />
      </DockIcon>
      <DockIcon aria-label="Settings">
        <Settings className="size-1/2" />
      </DockIcon>
    </Dock>
  );
}

The cursor position lives in a single useMotionValue on the dock. Each icon subscribes via useTransform, so React doesn't re-render on mousemove — Framer Motion writes the new size straight to the DOM.

Patterns

Tighter or looser magnification

Tune the resting size, peak size, and "feel radius" to fit your dock width. A smaller distance makes the effect feel sharp and local; a larger one makes it ripple across more icons.

<Dock baseSize={36} maxSize={56} distance={100}>{icons}</Dock>
<Dock baseSize={48} maxSize={96} distance={200}>{icons}</Dock>

Routing

DockIcon is a <motion.button> — wrap with your router link or pass onClick. For Next.js, render a <Link> around each <DockIcon> instead of using onClick.

<Link href="/inbox">
  <DockIcon aria-label="Inbox"><Mail className="size-1/2" /></DockIcon>
</Link>

Custom spring

Pass a spring to fine-tune the bounce. Lower mass + higher stiffness = snappier; higher damping = less overshoot.

<Dock spring={{ mass: 0.05, stiffness: 200, damping: 14 }}>{icons}</Dock>

Props

<Dock />

PropTypeDefaultDescription
baseSizenumber44Resting icon size in pixels.
maxSizenumber72Icon size at the exact cursor position.
distancenumber140Pixel radius around the cursor where icons start to magnify.
springSpringOptions{ mass: 0.1, stiffness: 150, damping: 12 }Framer Motion spring config for the scale animation.
classNamestringForwarded to the outer container.

<DockIcon />

DockIcon is a <motion.button> — every native button prop (onClick, aria-label, disabled, …) passes through. Set aria-label so screen readers can announce each icon.

Accessibility

  • Each icon is a real <button> with focus-visible styling and keyboard focus support.
  • The magnification effect is purely visual — it doesn't affect tab order, hit targets, or the accessibility tree.
  • Provide a meaningful aria-label for every icon since they have no visible text.