Docs
Dock

Dock

An implementation of the MacOS dock using react + tailwindcss + framer motion

Dock

Installation

Copy and paste the following code into your project.

components/magicui/dock.tsx
"use client";
import { cn } from "@/lib/utils";
import { cva, type VariantProps } from "class-variance-authority";
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion";
import React, { PropsWithChildren, useRef } from "react";
 
export interface DockProps extends VariantProps<typeof dockVariants> {
  className?: string;
  magnification?: number;
  distance?: number;
  children: React.ReactNode;
}
 
const DEFAULT_MAGNIFICATION = 60;
const DEFAULT_DISTANCE = 140;
 
const dockVariants = cva(
  "mx-auto w-max mt-8 h-[58px] p-2 flex items-end gap-2 rounded-2xl border dark:border-[#707070]",
);
 
const Dock = React.forwardRef<HTMLDivElement, DockProps>(
  (
    {
      className,
      children,
      magnification = DEFAULT_MAGNIFICATION,
      distance = DEFAULT_DISTANCE,
      ...props
    },
    ref,
  ) => {
    const mouseX = useMotionValue(Infinity);
 
    const renderChildren = () => {
      return React.Children.map(children, (child: any) => {
        return React.cloneElement(child, {
          mouseX: mouseX,
          magnification: magnification,
          distance: distance,
        });
      });
    };
 
    return (
      <motion.div
        ref={ref}
        onMouseMove={(e) => mouseX.set(e.pageX)}
        onMouseLeave={() => mouseX.set(Infinity)}
        {...props}
        className={cn(dockVariants({ className }), className)}
      >
        {renderChildren()}
      </motion.div>
    );
  },
);
 
Dock.displayName = "Dock";
 
export interface DockIconProps {
  size?: number;
  magnification?: number;
  distance?: number;
  mouseX?: any;
  className?: string;
  children?: React.ReactNode;
  props?: PropsWithChildren;
}
 
const DockIcon = ({
  size,
  magnification = DEFAULT_MAGNIFICATION,
  distance = DEFAULT_DISTANCE,
  mouseX,
  className,
  children,
  ...props
}: DockIconProps) => {
  const ref = useRef<HTMLDivElement>(null);
 
  const distanceCalc = useTransform(mouseX, (val: number) => {
    const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };
 
    return val - bounds.x - bounds.width / 2;
  });
 
  let widthSync = useTransform(
    distanceCalc,
    [-distance, 0, distance],
    [40, magnification, 40],
  );
 
  let width = useSpring(widthSync, {
    mass: 0.1,
    stiffness: 150,
    damping: 12,
  });
 
  return (
    <motion.div
      ref={ref}
      style={{ width }}
      className={cn(
        "flex aspect-square cursor-pointer items-center justify-center rounded-full bg-neutral-400/40",
        className,
      )}
      {...props}
    >
      {children}
    </motion.div>
  );
};
 
DockIcon.displayName = "DockIcon";
 
export { Dock, DockIcon, dockVariants };

Props

Dock

PropTypeDescriptionDefault
classNamestringCustom CSS class for styling-
childrenReactNodeChildren elements-
magnificationnumberLevel of icon magnification60
distancenumberDistance from cursor to magnify icon140

DockIcon

PropTypeDescriptionDefault
sizenumberSize of the icon-
magnificationnumberLevel of icon magnification60
distancenumberDistance from cursor to magnify icon140
mouseXanyMouse X position for magnification-
classNamestringCustom CSS class for styling-
childrenReact.ReactNodeChildren elements-
propsPropsWithChildrenAdditional props-

Credits

  • Credits to Build UI for this fantastic component
  • Credits to Ritesh Bucha for finding and improving it