CatalystUI

DocsTemplate

Profile Settings Card

A profile settings with links card often used as popover on avatar.

Installations

Install lucide-react, clsx, and tailwind-merge to use avatar component.

npm install lucide-react clsx tailwind-merge

Add the following function in lib/utils

import clsx, { type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
 
export function cx(...args: ClassValue[]) {
  return twMerge(clsx(...args));
}

Copy paste the component

import {
  LinkIcon,
  LogOutIcon,
  MoonIcon,
  SettingsIcon,
  UserIcon,
} from "lucide-react";
import { cx } from "@/lib/utils";
import Link from "next/link";
 
const profileLinks = [
  { icon: <UserIcon width={18} />, value: "Profile", link: "#" },
  { icon: <SettingsIcon width={18} />, value: "Account Settings", link: "#" },
  { icon: <LinkIcon width={18} />, value: "Integration", link: "#" },
  { icon: <MoonIcon width={18} />, value: "Dark Mode", link: "#" },
];
 
export default function ProfilePopoverContent({
  className,
}: {
  className?: string;
}) {
  return (
    <div
      className={cx(
        "border border-gray-200 dark:border-gray-800 py-2 w-3xs bg-white dark:bg-[#090E1A] rounded-lg text-sm",
        className
      )}
    >
      {profileLinks.map((link) => {
        if (link.value == "Dark Mode") {
          return (
            <div
              key="dark-mode-toggle"
              className="flex items-center justify-between pr-3 hover:bg-gray-100 dark:hover:bg-gray-600"
            >
              <NavLink
                key={link.value}
                link={{
                  icon: link.icon,
                  value: link.value,
                  link: link.link,
                }}
                className="hover:bg-gray-100 dark:hover:bg-gray-600 p-1 dark:text-gray-100"
              />
              <SwitchRoot>
                <SwitchThumb />
              </SwitchRoot>
            </div>
          );
        }
 
        return (
          <NavLink
            key={link.value}
            link={{
              icon: link.icon,
              value: link.value,
              link: link.link,
            }}
            className="hover:bg-gray-100 dark:hover:bg-gray-600 p-1 dark:text-gray-100"
          />
        );
      })}
      <hr className="text-gray-200 dark:text-gray-800 my-3" />
      <NavLink
        key="signout"
        link={{
          icon: <LogOutIcon width={18} />,
          value: "Sign Out",
          link: "#",
        }}
        className="hover:bg-gray-100 dark:hover:bg-gray-600 p-1 dark:text-gray-100"
      />
    </div>
  );
}
 
function NavLink({
  link,
  className,
}: {
  link: { icon: ReactElement; link: string; active?: boolean; value: string };
  className?: string;
}) {
  return (
    <Link
      href={link.link}
      className={cx(
        // base
        "flex gap-2 items-center",
        // active
        link.active
          ? "text-gray-950 dark:text-gray-50"
          : "text-gray-500 dark:text-gray-400",
        // class props
        className
      )}
    >
      <span
        className={`p-1 rounded-lg ${
          link.active ? "text-white bg-blue-600" : ""
        }`}
      >
        {link.icon}
      </span>{" "}
      {link.value}
    </Link>
  );
}

Usage

import ProfilePopoverContent from "@/library/blocks/profileCard";
 
export default function ProfileSettingsCard() {
  return (
    <CardRoot className="not-prose">
      <ProfilePopoverContent />
    </CardRoot>
  );
}

Next: Top Products Card

Found a bug? Report here.