Original Blocks/Application
Animated Dock
A dock with animated icons that grow on hover.
<AnimatedDock
items={[
{
link: "https://github.com/preetsuthar17",
target: "_blank",
Icon: <FaGithub size={22} />,
},
{
link: "https://x.com/preetsuthar17",
target: "_blank",
Icon: <FaXTwitter size={22} />,
},
{
link: "https://dsc.gg/hextastudio",
target: "_blank",
Icon: <FaDiscord size={22} />,
},
{
link: "https://linkedin.com/in/preetsuthar17",
target: "_blank",
Icon: <FaLinkedin size={22} />,
},
]}
/>
Installation
Copy and paste the following code into your project.
"use client";
import React, { useRef } from "react";
import {
MotionValue,
motion,
useMotionValue,
useSpring,
useTransform,
} from "motion/react";
import clsx from "clsx";
import { twMerge } from "tailwind-merge";
import Link from "next/link";
const cn = (...args: any[]) => twMerge(clsx(args));
export interface AnimatedDockProps {
className?: string;
items: DockItemData[];
}
export interface DockItemData {
link: string;
Icon: React.ReactNode;
target?: string;
}
export const AnimatedDock = ({ className, items }: AnimatedDockProps) => {
const mouseX = useMotionValue(Infinity);
return (
<motion.div
onMouseMove={(e) => mouseX.set(e.pageX)}
onMouseLeave={() => mouseX.set(Infinity)}
className={cn(
"mx-auto flex h-16 items-end gap-4 rounded-[var(--radius)] bg-[hsl(var(--hu-card))] border border-[hsl(var(--hu-border))] px-4 pb-3",
className,
)}
>
{items.map((item, index) => (
<DockItem key={index} mouseX={mouseX}>
<Link
href={item.link}
target={item.target}
className="grow flex items-center justify-center w-full h-full text-[hsl(var(--hu-primary-foreground))]"
>
{item.Icon}
</Link>
</DockItem>
))}
</motion.div>
);
};
interface DockItemProps {
mouseX: MotionValue<number>;
children: React.ReactNode;
}
export const DockItem = ({ mouseX, children }: DockItemProps) => {
const ref = useRef<HTMLDivElement>(null);
const distance = useTransform(mouseX, (val) => {
const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };
return val - bounds.x - bounds.width / 2;
});
const widthSync = useTransform(distance, [-150, 0, 150], [40, 80, 40]);
const width = useSpring(widthSync, {
mass: 0.1,
stiffness: 150,
damping: 12,
});
const iconScale = useTransform(width, [40, 80], [1, 1.5]);
const iconSpring = useSpring(iconScale, {
mass: 0.1,
stiffness: 150,
damping: 12,
});
return (
<motion.div
ref={ref}
style={{ width }}
className="aspect-square w-10 rounded-full bg-[hsl(var(--hu-primary))] text-[hsl(var(--hu-primary-foreground))] flex items-center justify-center"
>
<motion.div
style={{ scale: iconSpring }}
className="flex items-center justify-center w-full h-full grow"
>
{children}
</motion.div>
</motion.div>
);
};
npx shadcn@latest add "https://21st.dev/r/hextaui/animated-dock"
pnpm dlx shadcn@latest add "https://21st.dev/r/hextaui/animated-dock"
yarn dlx shadcn@latest add "https://21st.dev/r/hextaui/animated-dock"
bun x shadcn@latest add "https://21st.dev/r/hextaui/animated-dock"
Usage
import { AnimatedDock } from "@/components/ui/animated-dock";
import { FaDiscord, FaGithub, FaLinkedin } from "react-icons/fa";
import { FaXTwitter } from "react-icons/fa6";
<AnimatedDock
items={[
{
link: "https://github.com/preetsuthar17",
target: "_blank",
Icon: <FaGithub size={22} />,
},
{
link: "https://x.com/preetsuthar17",
target: "_blank",
Icon: <FaXTwitter size={22} />,
},
{
link: "https://dsc.gg/hextastudio",
target: "_blank",
Icon: <FaDiscord size={22} />,
},
{
link: "https://linkedin.com/in/preetsuthar17",
target: "_blank",
Icon: <FaLinkedin size={22} />,
},
]}
/>
Props
AnimatedDock Props
Prop | Type | Default |
---|---|---|
className? | string | "" |
items? | DockItemData[] | [] |
DockItemData Props
Prop | Type | Default |
---|---|---|
target? | string | "_self" |
Icon? | React.ReactNode | <FaGithub size={22} /> |
link? | string | "" |
Edit on GitHub
Last updated on