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
Prop | Type | Description | Default |
---|---|---|---|
className | string | Custom CSS class for styling | - |
children | ReactNode | Children elements | - |
magnification | number | Level of icon magnification | 60 |
distance | number | Distance from cursor to magnify icon | 140 |
DockIcon
Prop | Type | Description | Default |
---|---|---|---|
size | number | Size of the icon | - |
magnification | number | Level of icon magnification | 60 |
distance | number | Distance from cursor to magnify icon | 140 |
mouseX | any | Mouse X position for magnification | - |
className | string | Custom CSS class for styling | - |
children | React.ReactNode | Children elements | - |
props | PropsWithChildren | Additional props | - |
Credits
- Credits to Build UI for this fantastic component
- Credits to Ritesh Bucha for finding and improving it