We are working on new components <3
HextaUIHextaUI
ApplicationAnimated Dock

Animated Dock

A dock with animated icons that grow on hover.

Preview

Code

AnimatedDock.tsx
"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[]) => {
  return twMerge(clsx(args));
};
 
interface AnimatedDockProps {
  className?: string;
  items: DockItemData[];
}
 
interface DockItemData {
  link: string;
  Icon: React.ReactNode;
  target?: string;
}
 
export const AnimatedDock = ({ className, items }: AnimatedDockProps) => {
  let 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-2xl bg-primary/10 shadow-md border-opacity-5 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-primary-foreground"
          >
            {item.Icon}
          </Link>
        </DockItem>
      ))}
    </motion.div>
  );
};
 
export const DockItem = ({
  mouseX,
  children,
}: {
  mouseX: MotionValue;
  children: React.ReactNode;
}) => {
  let ref = useRef<HTMLDivElement>(null);
 
  let distance = useTransform(mouseX, (val) => {
    let bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };
 
    return val - bounds.x - bounds.width / 2;
  });
 
  let widthSync = useTransform(distance, [-150, 0, 150], [40, 80, 40]);
  let width = useSpring(widthSync, { mass: 0.1, stiffness: 150, damping: 12 });
 
  return (
    <motion.div
      ref={ref}
      style={{ width }}
      className="aspect-square w-10 rounded-full bg-primary text-secondary-foreground flex items-center justify-center "
    >
      <div className="flex items-center justify-center w-full h-full grow">
        {children}
      </div>
    </motion.div>
  );
};

Usage

Page.tsx
import { FaDiscord, FaGithub, FaLinkedin } from "react-icons/fa";
import { FaXTwitter } from "react-icons/fa6";
import { AnimatedDock } from "@/components/library/application/animated-dock";
 
<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

PropTypeDefault
className
string
-
items
DockItemData[]
-

DockItem Props

PropTypeDefault
link
string
-
Icon
ReactNode
-
target
string
-
Edit on GitHub

Last updated on

On this page