HextaUIHextaUI
/Components/Application

Profile Dropdown

Animated profile dropdown component

Preview

Installation

ProfileDropdown.tsx
"use client";

import * as React from "react";
import { useEffect, useRef, useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import {
  User,
  CreditCard,
  Settings,
  Headphones,
  MessageCircle,
  Repeat,
  LogOut,
} from "lucide-react";

const ProfileDropdown = () => {
  const [isOpen, setIsOpen] = useState(false);
  const menuRef = useRef(null);

  useEffect(() => {
    function handleClickOutside(event: MouseEvent): void {
      if (
        menuRef.current &&
        !(menuRef.current as HTMLElement).contains(event.target as Node)
      ) {
        setIsOpen(false);
      }
    }
    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [menuRef]);

  return (
    <div className="relative text-left flex items-start justify-end h-full w-full">
      <button
        onClick={() => setIsOpen(!isOpen)}
        className="focus:outline-none rounded-full w-10 h-10 flex items-center justify-center shadow-md"
      >
        <img src="/logo.png" alt="Profile" className="rounded-full w-10 h-10" />
      </button>

      <AnimatePresence>
        {isOpen && (
          <motion.div
            initial={{ opacity: 0, scale: 0.9 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={{ opacity: 0, scale: 0.9 }}
            transition={{
              type: "spring",
              stiffness: 300,
              damping: 20,
            }}
            className="absolute top-12 mt-3 w-64 sm:w-72 bg-white dark:bg-secondary text-secondary-foreground rounded-2xl shadow-xl p-4 space-y-4 z-50 right-0"
            ref={menuRef}
          >
            <div className="flex items-center gap-3 border-b border-secondary-foreground/10 pb-4">
              <div className="relative shadow-xl rounded-full">
                <img
                  src="/logo.png"
                  alt="User"
                  className="w-10 h-10 rounded-full"
                />
                {/* Online badge */}
                <span className="absolute bottom-0 right-0 block w-3 h-3 bg-green-500 border-2 border-white dark:border-gray-900 rounded-full"></span>
              </div>
              <div className="flex flex-col">
                <h4 className="font-semibold text-sm sm:text-base">HextaUI</h4>
                <p className="text-xs sm:text-sm">hi@hextastudio.com</p>
              </div>
            </div>

            <div className="flex flex-col my-2">
              <MenuItem icon={<User size={18} />} text="View profile" />
              <MenuItem icon={<CreditCard size={18} />} text="Subscriptions" />
              <MenuItem icon={<Settings size={18} />} text="Settings" />
            </div>

            <div className="flex flex-col my-2 pt-2 border-t border-secondary-foreground/10">
              <MenuItem icon={<Headphones size={18} />} text="Support" />
              <MenuItem icon={<MessageCircle size={18} />} text="Community" />
            </div>

            <div className="flex flex-col mt-2 pt-2 border-t border-secondary-foreground/10">
              <MenuItem icon={<Repeat size={18} />} text="Switch account" />
              <MenuItem icon={<LogOut size={18} />} text="Sign out" />
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
};

interface MenuItemProps {
  icon: React.ReactNode;
  text: string;
}

function MenuItem({ icon, text }: MenuItemProps) {
  return (
    <div className="flex items-center gap-2 hover:bg-primary/10 transition p-2 rounded-xl cursor-pointer opacity-80">
      <div>{icon}</div>
      <span className="text-xs sm:text-sm font-medium">{text}</span>
    </div>
  );
}

export { ProfileDropdown };

Usage

App.tsx
const ProfileDropdownExample = () => {
  return (
    <div className="h-1/2 flex items-center justify-start text-center flex-col w-full">
      <nav className="rounded-full shadow-lg/5 bg-white dark:bg-secondary text-secondary-foreground  max-w-2xl w-full">
        <div className="container mx-auto px-4 py-3 flex justify-between items-center">
          <div className="text-lg font-bold text-primary pl-4">HextaUI</div>
          <div className="flex items-center gap-4 max-md:hidden">
            <a href="#" className="text-sm hover:text-primary">
              Home
            </a>
            <a href="#" className="text-sm hover:text-primary">
              About
            </a>
            <a href="#" className="text-sm hover:text-primary">
              Contact
            </a>
            <ProfileDropdown />
          </div>
        </div>
      </nav>
    </div>
  );
};

export { ProfileDropdownExample };
Edit on GitHub

Last updated on