Components
Dock
A macOS-style dock component with magnification effects and smooth animations.
Last updated on
Basic Usage
import { Calculator, Calendar, Compass, Mail, Music } from "lucide-react";
import {
Dock,
DockIcon,
DockItem,
DockLabel,
} from "@/components/ui/dock";
export function DockDemo() {
return (
<div className="flex h-32 items-end justify-center">
<Dock>
<DockItem>
<DockIcon>
<Compass />
</DockIcon>
<DockLabel>Safari</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Mail />
</DockIcon>
<DockLabel>Mail</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Calendar />
</DockIcon>
<DockLabel>Calendar</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Music />
</DockIcon>
<DockLabel>Music</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Calculator />
</DockIcon>
<DockLabel>Calculator</DockLabel>
</DockItem>
</Dock>
</div>
);
}macOS Style
import {
Calculator,
Calendar,
Camera,
ChromeIcon,
Clock,
Image,
Mail,
MessageSquare,
Music,
Notebook,
Settings,
Store,
Terminal,
Trash2,
} from "lucide-react";
import {
Dock,
DockIcon,
DockItem,
DockLabel,
DockSeparator,
} from "@/components/ui/dock";
export function DockMacosDemo() {
return (
<div className="flex h-32 items-end justify-center">
<Dock magnification={80} distance={150}>
<DockItem>
<DockIcon>
<ChromeIcon />
</DockIcon>
<DockLabel>Chrome</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Calendar />
</DockIcon>
<DockLabel>Calendar</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Mail />
</DockIcon>
<DockLabel>Mail</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<MessageSquare />
</DockIcon>
<DockLabel>Messages</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Music />
</DockIcon>
<DockLabel>Music</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Image />
</DockIcon>
<DockLabel>Photos</DockLabel>
</DockItem>
<DockSeparator />
<DockItem>
<DockIcon>
<Store />
</DockIcon>
<DockLabel>App Store</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Calculator />
</DockIcon>
<DockLabel>Calculator</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Clock />
</DockIcon>
<DockLabel>Clock</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Notebook />
</DockIcon>
<DockLabel>Notes</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Terminal />
</DockIcon>
<DockLabel>Terminal</DockLabel>
</DockItem>
<DockItem>
<DockIcon>
<Settings />
</DockIcon>
<DockLabel>System Settings</DockLabel>
</DockItem>
<DockSeparator />
<DockItem>
<DockIcon>
<Camera />
</DockIcon>
<DockLabel>Camera</DockLabel>
</DockItem>
<DockItem>
<DockIcon className="text-red-500">
<Trash2 />
</DockIcon>
<DockLabel>Trash</DockLabel>
</DockItem>
</Dock>
</div>
);
}Social Media
import {
Dock,
DockIcon,
DockItem,
DockLabel,
} from "@/components/ui/dock";
import {
Facebook,
Github,
Instagram,
Linkedin,
Twitter,
Youtube,
} from "lucide-react";
export function DockSocialDemo() {
return (
<div className="flex h-32 items-end justify-center">
<Dock
magnification={60}
distance={100}
className="bg-transparent border-none shadow-none"
>
<DockItem className="bg-black/5 dark:bg-white/10">
<DockIcon>
<Github />
</DockIcon>
<DockLabel>GitHub</DockLabel>
</DockItem>
<DockItem className="bg-blue-500/10 text-blue-500">
<DockIcon>
<Twitter />
</DockIcon>
<DockLabel>Twitter</DockLabel>
</DockItem>
<DockItem className="bg-pink-500/10 text-pink-500">
<DockIcon>
<Instagram />
</DockIcon>
<DockLabel>Instagram</DockLabel>
</DockItem>
<DockItem className="bg-blue-700/10 text-blue-700">
<DockIcon>
<Linkedin />
</DockIcon>
<DockLabel>LinkedIn</DockLabel>
</DockItem>
<DockItem className="bg-blue-600/10 text-blue-600">
<DockIcon>
<Facebook />
</DockIcon>
<DockLabel>Facebook</DockLabel>
</DockItem>
<DockItem className="bg-red-600/10 text-red-600">
<DockIcon>
<Youtube />
</DockIcon>
<DockLabel>YouTube</DockLabel>
</DockItem>
</Dock>
</div>
);
}Installation
CLI
npx shadcn@latest add "https://jolyui.dev/r/dock"Manual
Install the following dependencies:
npm install motion lucide-reactCopy and paste the following code into your project. component/ui/dock.tsx
import { motion, useMotionValue, useSpring, useTransform } from "motion/react";
import * as React from "react";
import { cn } from "@/lib/utils";
interface DockProps {
/** Distance threshold for magnification effect */
magnification?: number;
/** Maximum scale factor when hovered */
maxScale?: number;
/** Base size of dock items in pixels */
iconSize?: number;
/** Distance from mouse to apply magnification */
distance?: number;
className?: string;
children?: React.ReactNode;
}
const DockContext = React.createContext<{
mouseX: ReturnType<typeof useMotionValue<number>>;
magnification: number;
maxScale: number;
iconSize: number;
distance: number;
} | null>(null);
const Dock = React.forwardRef<HTMLDivElement, DockProps>(
(
{
className,
children,
magnification = 60,
maxScale = 1.5,
iconSize = 48,
distance = 140,
},
ref,
) => {
const mouseX = useMotionValue(Infinity);
return (
<DockContext.Provider
value={{ mouseX, magnification, maxScale, iconSize, distance }}
>
<motion.div
ref={ref}
onMouseMove={(e) => mouseX.set(e.pageX)}
onMouseLeave={() => mouseX.set(Infinity)}
className={cn(
"dock mx-auto flex h-16 items-end gap-2 rounded-2xl border bg-background/80 px-3 pb-2 backdrop-blur-md",
"shadow-foreground/5 shadow-lg",
className,
)}
>
{children}
</motion.div>
</DockContext.Provider>
);
},
);
Dock.displayName = "Dock";
interface DockItemProps {
className?: string;
children?: React.ReactNode;
onClick?: () => void;
}
const DockItem = React.forwardRef<HTMLDivElement, DockItemProps>(
({ className, children, onClick }, _ref) => {
const context = React.useContext(DockContext);
const itemRef = React.useRef<HTMLDivElement>(null);
if (!context) {
throw new Error("DockItem must be used within a Dock");
}
const { mouseX, maxScale, iconSize, distance } = context;
const distanceCalc = useTransform(mouseX, (val: number) => {
const bounds = itemRef.current?.getBoundingClientRect() ?? {
x: 0,
width: 0,
};
return val - bounds.x - bounds.width / 2;
});
const widthSync = useTransform(
distanceCalc,
[-distance, 0, distance],
[iconSize, iconSize * maxScale, iconSize],
);
const width = useSpring(widthSync, {
mass: 0.1,
stiffness: 150,
damping: 12,
});
return (
<motion.div
ref={itemRef}
style={{ width, height: width }}
onClick={onClick}
className={cn(
"dock-item group relative flex aspect-square cursor-pointer items-center justify-center rounded-xl bg-muted transition-colors hover:bg-muted/80",
className,
)}
>
{children}
</motion.div>
);
},
);
DockItem.displayName = "DockItem";
interface DockIconProps {
className?: string;
children?: React.ReactNode;
}
const DockIcon = React.forwardRef<HTMLDivElement, DockIconProps>(
({ className, children }, ref) => {
return (
<div
ref={ref}
className={cn(
"dock-icon flex h-full w-full items-center justify-center text-foreground",
"[&>svg]:h-1/2 [&>svg]:w-1/2",
className,
)}
>
{children}
</div>
);
},
);
DockIcon.displayName = "DockIcon";
interface DockLabelProps {
className?: string;
children?: React.ReactNode;
}
const DockLabel = React.forwardRef<HTMLDivElement, DockLabelProps>(
({ className, children }, ref) => {
return (
<div
ref={ref}
className={cn(
"dock-label -top-9 -translate-x-1/2 pointer-events-none absolute left-1/2 whitespace-nowrap rounded-md bg-foreground px-2 py-1 text-background text-xs opacity-0 transition-opacity group-hover:opacity-100",
className,
)}
>
{children}
{/* Tooltip arrow */}
<div className="-bottom-1 -translate-x-1/2 absolute left-1/2 h-2 w-2 rotate-45 bg-foreground" />
</div>
);
},
);
DockLabel.displayName = "DockLabel";
interface DockSeparatorProps {
className?: string;
}
const DockSeparator = React.forwardRef<HTMLDivElement, DockSeparatorProps>(
({ className }, ref) => {
return (
<div
ref={ref}
className={cn(
"dock-separator mx-1 h-10 w-px self-center bg-border",
className,
)}
/>
);
},
);
DockSeparator.displayName = "DockSeparator";
export {
Dock,
DockIcon,
DockItem,
DockLabel,
DockSeparator,
type DockIconProps,
type DockItemProps,
type DockLabelProps,
type DockProps,
type DockSeparatorProps,
};Usage
import { Dock, DockIcon, DockItem, DockLabel } from "@/components/ui/dock";
function MyDock() {
return (
<Dock>
<DockItem>
<DockIcon>
<HomeIcon />
</DockIcon>
<DockLabel>Home</DockLabel>
</DockItem>
{/* More items... */}
</Dock>
);
}API Reference
Dock
Prop
Type
DockItem
Prop
Type
DockIcon
Prop
Type
DockLabel
Prop
Type
DockSeparator
Prop
Type
Notes
- The dock automatically applies magnification effects when hovering near items
- Use
DockSeparatorto group related items - Icons are automatically sized and centered within
DockIcon - Labels appear as tooltips on hover
- The component uses Framer Motion for smooth animations
How is this guide?