Skip to content
Black FridayBlack Friday launch price: Lifetime $149 until 30 Nov. Regular price $299 from 1 Dec.ends in 15d 03:13:35Get Lifetime →
loopkit
proNew318 lines · framer-motion

Navbar Dock

A dock that leans toward your cursor. A macOS-dock-style icon navigation anchored along the top or bottom edge of a short framed backdrop, with icons that magnify based on pointer proximity and a flat, tappable row on touch screens.

Use it for
An app shell, control surface or desktop-style product demo that wants a playful, tactile primary navigation instead of a flat top bar.
Themes
Light + dark
License
Commercial use
Live
Navigation

Jump anywhere without losing your place

The same six destinations stay one tap away, wherever you are in the product.

tsx
"use client";

import * as React from "react";
import { AnimatePresence, motion, useMotionValue, useReducedMotion, useSpring, useTransform } from "framer-motion";
import { cn } from "@/lib/utils";

function useReducedMotionSafe() {
  const prefersReduced = useReducedMotion();
  const [mounted, setMounted] = React.useState(false);
  React.useEffect(() => setMounted(true), []);
  return mounted ? !!prefersReduced : false;
}

/**
 * A macOS-dock-style icon navigation: icons magnify based on pointer proximity
 * (distance from pointer X to each icon's centre, mapped to a spring-driven size).
 * Anchored `absolute` along the top or bottom edge of a short framed backdrop so it
 * never escapes the block. Below ~640px there is no reliable pointer, so a flat,
 * evenly sized, comfortably tappable row replaces the magnifying one.
 */

export type DockIconId = "home" | "search" | "messages" | "files" | "insights" | "settings";

The rest is in Pro.

Full source, the rebuild prompt and registry install unlock with Pro or Lifetime.

Get Pro

How it behaves

  • →A shared framer-motion MotionValue tracks the pointer's X position inside the dock; each icon derives its distance to the pointer from its own getBoundingClientRect and maps that distance to a target size with useTransform, smoothed through a useSpring so growth/shrink feels elastic, not stepped.
  • →magnification scales how large the nearest icon grows relative to baseSize; baseSize sets the resting icon size.
  • →The dock is positioned absolute at the top or bottom edge of its own framed backdrop (position prop), never fixed, so it stays inside the block at any zoom.
  • →Hovering or focusing an icon reveals a small label tooltip positioned absolute above (bottom-anchored dock) or below (top-anchored dock) that icon, inside the icon's own relative wrapper.
  • →Below the sm breakpoint, or for any non-mouse pointer, the magnifying dock is replaced by a flat row of evenly sized, comfortably tappable icon buttons with no hover dependency.
  • →activeId highlights one icon (border, tint and a small indicator dot) and is kept in sync with internal click state; onSelect fires with the clicked icon's id.

Accessibility

  • ✓Every icon is a real `<button>` with `aria-label` and `aria-pressed` reflecting the active state; the SVG icons are `aria-hidden`.
  • ✓Tooltips also appear on keyboard focus (`onFocus`/`onBlur`), not only on pointer hover, so keyboard users get the same label.
  • ✓The mobile flat row keeps all 6 icons at a 44px+ tap target with no reliance on hover or pointer proximity.
  • ✓Pointer tracking only runs for mouse input (`pointerType === "mouse"`) and is skipped entirely under `prefers-reduced-motion`, which renders every icon at a fixed, static size.
  • ✓Honours prefers-reduced-motion.