Dock
ui-beats/dockFreeComponent
The Dock component recreates the macOS dock: items swell as the pointer approaches, and each one names itself in a tooltip on hover or focus.
Install it
npx shadcn@latest add https://uibeats.com/r/dock.jsonSource
1"use client";23import {4 AnimatePresence,5 motion,6 useMotionValue,7 useReducedMotion,8 useSpring,9 useTransform,10 type MotionValue,11} from "motion/react";12import {13 createContext,14 useContext,15 useRef,16 useState,17 type ReactNode,18} from "react";1920interface DockContextValue {21 mouseX: MotionValue<number>;22 size: number;23 magnification: number;24 reach: number;25}2627const DockContext = createContext<DockContextValue | null>(null);2829function useDock(component: string) {30 const context = useContext(DockContext);31 if (!context) {32 throw new Error(`<${component}> must be rendered inside a <Dock>.`);33 }34 return context;35}3637interface DockProps {38 children: ReactNode;39 /** Resting size of each item, in pixels. */40 size?: number;41 /** Size an item reaches directly under the pointer. */42 magnification?: number;43 /** How far, in pixels, the magnification reaches along the dock. */44 reach?: number;45 className?: string;46}4748/**49 * A macOS-style dock: items swell as the pointer approaches and settle back50 * on a spring as it leaves.51 *52 * The pointer's x position is held in a motion value and read by each item53 * through context, so magnification runs entirely off the main thread — no54 * state updates and no re-renders per frame, however many items the dock55 * holds.56 */57export function Dock({58 children,59 size = 44,60 magnification = 76,61 reach = 130,62 className = "",63}: DockProps) {64 // Infinity parks every item at its resting size until the pointer arrives.65 const mouseX = useMotionValue(Number.POSITIVE_INFINITY);66 const prefersReducedMotion = useReducedMotion();6768 return (69 <DockContext.Provider70 value={{71 mouseX,72 size,73 magnification: prefersReducedMotion ? size : magnification,74 reach,75 }}76 >77 <div78 role="toolbar"79 aria-label="Dock"80 onPointerMove={(event) => {81 if (event.pointerType === "touch" || prefersReducedMotion) return;82 mouseX.set(event.clientX);83 }}84 onPointerLeave={() => mouseX.set(Number.POSITIVE_INFINITY)}85 style={{ height: magnification + 16 }}86 className={`flex items-end gap-3 rounded-2xl border bg-card/70 px-4 pb-3 backdrop-blur-xl ${className}`}87 >88 {children}89 </div>90 </DockContext.Provider>91 );92}9394interface DockItemProps {95 children: ReactNode;96 /** Name shown in the tooltip above the item, and its accessible name. */97 label: string;98 onClick?: () => void;99// … truncated
What it pulls in
npm packages
Files it writes
More from ui-beats
All 26 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Beamanimated-beam | ui-beats | Components | Free | 1 dep | |
| Bouncebounce | ui-beats | Components | Free | 1 dep | |
| Fade Infade-in | ui-beats | Components | Free | 1 dep | |
| Fade In Unblurfade-in-unblur | ui-beats | Components | Free | 1 dep | |
| Flip Cardflip-card | ui-beats | Components | Free | 2 deps | |
| Glowing Cardglowing-card | ui-beats | Components | Free | 1 dep | |
| Gradient Flowgradient-flow | ui-beats | Components | Free | 1 dep | |
| Gravity Text Swapgravity-text-swap | ui-beats | Components | Free | 1 dep |
