Navigation

Dock

A macOS-style magnifying dock — icons scale smoothly based on their horizontal distance from the cursor, with labels floating up on hover.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add dock.en

New project or installation trouble? Start here →

Dock is a React macOS-style dock (magnification dock) component built with Tailwind CSS and Motion. As the mouse approaches, icons scale up smoothly with a spring based on horizontal distance, and a label pops up above an icon on hover or keyboard focus.

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

64
40
140
Active edited usageDefault props
<Dock/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

When to use

  • A fixed bottom navigation bar on a portfolio or personal site, holding social links and key pages.
  • Toolbars in editor or whiteboard apps, such as pen, select, text and export.
  • A desktop shortcut bar on a mobile-first site; on touch it stays a row of regular-sized buttons.
  • Not a good fit: main menus with more than about ten items or ones that need always-visible text labels; a regular navbar or sidebar is easier to scan.

Installation

npx webberui@latest add https://webberui.com/r/dock.en.json

Installing also writes the --wb-duration-fast CSS variable into your global stylesheet.

Usage

import { Dock, DockItem } from "@/components/ui/dock";
import { Home, Search, Settings } from "lucide-react";

<Dock>
  <DockItem label="Home" href="/">
    <Home />
  </DockItem>
  <DockItem label="Search" onClick={() => console.log("search")}>
    <Search />
  </DockItem>
  <DockItem label="Settings" href="/settings">
    <Settings />
  </DockItem>
</Dock>

Examples

Fixed to the bottom of the screen

Add positioning classes to className to pin the dock to the bottom center of the viewport.

import { Dock, DockItem } from "@/components/ui/dock";
import { Home, FolderOpen, Mail } from "lucide-react";

export function BottomDock() {
  return (
    <Dock className="fixed bottom-6 left-1/2 z-50 -translate-x-1/2">
      <DockItem label="Home" href="/">
        <Home />
      </DockItem>
      <DockItem label="Projects" href="/projects">
        <FolderOpen />
      </DockItem>
      <DockItem label="Contact" href="mailto:hello@example.com">
        <Mail />
      </DockItem>
    </Dock>
  );
}

Client-side navigation with Next.js

href renders a plain <a>. To use Next.js client-side routing, leave out href and call router.push in onClick instead.

"use client";

import { useRouter } from "next/navigation";
import { Dock, DockItem } from "@/components/ui/dock";
import { LayoutDashboard, Settings } from "lucide-react";

export function AppDock() {
  const router = useRouter();
  return (
    <Dock>
      <DockItem label="Overview" onClick={() => router.push("/dashboard")}>
        <LayoutDashboard />
      </DockItem>
      <DockItem label="Settings" onClick={() => router.push("/settings")}>
        <Settings />
      </DockItem>
    </Dock>
  );
}

Tune magnification and range

baseSize is the resting size, magnification the maximum size and distance the horizontal radius the cursor affects, all in px.

<Dock baseSize={48} magnification={80} distance={180}>
  {/* DockItem… */}
</Dock>

Props

Dock

PropTypeDefaultDescription
magnificationnumber64Icon size when the cursor is closest (px)
baseSizenumber40Icon size at rest (px)
distancenumber140Radius of the magnification effect (px): icons only scale up when the cursor is within this horizontal distance of their center
classNamestring—Appended to the dock container's className
childrenReact.ReactNode—Put DockItem children directly inside

DockItem

PropTypeDefaultDescription
labelstring—Icon label: shown above on hover or keyboard focus, and used as the aria-label
onClickReact.MouseEventHandler—Click event (works for both the button and the link)
hrefstring—When provided, renders as an <a> link; otherwise a <button>
childrenReact.ReactNode—Icon content: any SVG or element; it automatically fills the icon area and scales proportionally with the magnification
classNamestring—Appended to the individual icon button's className

How it works

  • Shared mouseX: the container's pointermove updates a shared cursor X coordinate (a MotionValue), set to Infinity when the pointer leaves; every DockItem uses useTransform to map "horizontal distance from the cursor" onto a size range, then smooths it with useSpring, producing the classic macOS magnification wave
  • The dock itself never jumps: the container height is fixed at baseSize plus padding with the icons bottom-aligned, so magnification overflows upward
  • Tooltip labels: hovering a single icon floats a small card above it via AnimatePresence; the label is also the aria-label, and the card itself is hidden from assistive technology so nothing is read twice
  • Keyboard accessibility: DockItem is a native <button> (an <a> when href is present), so Tab focuses them one by one; on focus-visible it magnifies to magnification and shows the label just the same
  • Automatic touch fallback: it only responds to mouse pointer events (pointerType === "mouse"), so on touch it is simply a row of equally sized ordinary buttons
  • When the user has "reduce motion" enabled at the system level, the magnification and label displacement animations are disabled; the DOM structure is completely unchanged and functionality is unaffected
  • The component itself does not depend on any icon library — whatever SVG you put in children automatically fills the icon area

FAQ

Does it work with the Next.js App Router?

Yes. The file starts with "use client", so import it from a Server Component page as usual. If you navigate with useRouter, the component that calls it must be a Client Component too.

Do I have to use lucide-react?

No. The component itself depends on no icon library, and any SVG you put in a DockItem's children fills the icon area and scales with it. The Usage example uses lucide-react, so install it separately if you copy that code.

Does it magnify on phones?

No. Magnification only responds to mouse pointer events; on touch it is a row of equal-sized buttons and taps work as usual.

Can keyboard and screen reader users operate it?

DockItem is a native <button>, or an <a> when href is set, so Tab moves through the items, and keyboard focus magnifies the item and shows its label. label also serves as the aria-label, and the floating label card is hidden from assistive technology so it is not read twice.

What happens with reduced motion?

Magnification and the label slide animation are turned off. The DOM structure stays the same and links and buttons work normally.