Mac Dock
Free Docks React component — MacDock. MIT licensed, copy-paste ready for Next.js and Tailwind CSS.
Frosted macOS-style app dock with smooth hover lift, tooltips, and brand icons — Tailwind-only, no motion library.
Preview
Setup
How to use
Free for personal and commercial use. No UI attribution required. Include the MIT copyright notice when copying component source into your project. Read the MIT license.
- 1
Run in your terminal:
$npm install clsx tailwind-merge lucide-react - 2
Copy
lib/cn.tsbelow. Skip if you already havecn(). - 3
Copy the code below and create
components/docks/mac-dock.tsxin your project. - 4
Import and render:
Exampleimport { MacDock } from "@/components/docks/mac-dock";
lib/cn.ts
import { type ClassValue, clsx } from "clsx";import { twMerge } from "tailwind-merge";export function cn(...inputs: ClassValue[]) {return twMerge(clsx(inputs));}
components/docks/mac-dock.tsx
"use client";import {forwardRef,type ComponentPropsWithoutRef,type ReactNode,} from "react";import Link from "next/link";import { cn } from "@/lib/cn";import {AdobePhotoshopIcon,AppleAppStoreIcon,AppleMusicIcon,AppleSafariIcon,NotionCalendarIcon,SpotifyIcon,} from "./Icons";export type MacDockItem = Readonly<{title: string;icon: ReactNode;href: string;}>;export type MacDockProps = Readonly<{items?: readonly MacDockItem[];desktopClassName?: string;} & Omit<ComponentPropsWithoutRef<"div">, "children">>;const DEFAULT_ITEMS: readonly MacDockItem[] = [{title: "Adobe Photoshop",href: "#",icon: <AdobePhotoshopIcon className="size-full" />,},{title: "Spotify",href: "#",icon: <SpotifyIcon className="size-full" />,},{title: "Notion Calendar",href: "#",icon: <NotionCalendarIcon className="size-full" />,},{title: "Apple Music",href: "#",icon: <AppleMusicIcon className="size-full" />,},{title: "App Store",href: "#",icon: <AppleAppStoreIcon className="size-full" />,},{title: "Safari",href: "#",icon: <AppleSafariIcon className="size-full" />,},];function DockLink({href,className,children,ariaLabel,}: Readonly<{href: string;className?: string;children: ReactNode;ariaLabel: string;}>) {if (!href || href === "#") {return (<button type="button" aria-label={ariaLabel} className={className}>{children}</button>);}return (<Link href={href} aria-label={ariaLabel} className={className}>{children}</Link>);}function DockIcon({ item }: Readonly<{ item: MacDockItem }>) {return (<DockLinkhref={item.href}ariaLabel={item.title}className="group relative z-10 flex flex-col items-center outline-none"><span className="pointer-events-none absolute -top-10 left-1/2 z-20 -translate-x-1/2 rounded-md bg-neutral-950 px-2 py-0.5 text-[10px] font-medium whitespace-nowrap text-white opacity-0 shadow-sm transition-[opacity,transform] duration-200 ease-smooth group-hover:-translate-y-0.5 group-hover:opacity-100 group-focus-visible:opacity-100">{item.title}</span>{/* Transform on the outer wrapper — backdrop-blur fails if transform/will-change sit on the same node. */}<span className="origin-bottom cursor-pointer transition-transform duration-300 ease-smooth group-hover:-translate-y-2 group-hover:scale-125 group-focus-visible:-translate-y-2 group-focus-visible:scale-125"><span className="flex size-11 items-center justify-center rounded-lg border border-white/20 bg-white/50 shadow-xs shadow-black/5 backdrop-blur-xl"><span className="size-8">{item.icon}</span></span></span></DockLink>);}// Mac dock — frosted app tray with smooth hover lift (design reference for docks/).export const MacDock = forwardRef<HTMLDivElement, MacDockProps>(({className,items = DEFAULT_ITEMS,desktopClassName,...props},ref,) => (<divref={ref}data-slot="mac-dock"className={cn("relative overflow-visible pt-10 font-sans", className)}{...props}><navaria-label="Application dock"className={cn("relative mx-auto inline-flex items-end gap-2.5 p-3",desktopClassName,)}>{/* Tray glass as a sibling layer so icon tiles can blur the page behind, not a nested filter. */}<spanaria-hiddenclassName="pointer-events-none absolute inset-0 rounded-2xl border border-white/20 bg-white/10 shadow-xl shadow-black/10 backdrop-blur-md"/>{items.map((item) => (<DockIcon key={item.title} item={item} />))}</nav></div>),);MacDock.displayName = "MacDock";