{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "clerk-user-button",
  "title": "Clerk User Button",
  "description": "User avatar with dropdown menu showing profile, settings, and sign-out options. Features initials fallback and click-outside-to-close.",
  "dependencies": [
    "@clerk/nextjs"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/clerk/clerk-user-button/components/elements/clerk-user-button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useClerk, useUser } from \"@clerk/nextjs\";\n\nfunction cn(...classes: (string | boolean | undefined)[]) {\n  return classes.filter(Boolean).join(\" \");\n}\n\nfunction UserIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n    >\n      <path d=\"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2\" />\n      <circle cx=\"12\" cy=\"7\" r=\"4\" />\n    </svg>\n  );\n}\n\nfunction SettingsIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n    >\n      <path d=\"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z\" />\n      <circle cx=\"12\" cy=\"12\" r=\"3\" />\n    </svg>\n  );\n}\n\nfunction LogOutIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n    >\n      <path d=\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\" />\n      <polyline points=\"16 17 21 12 16 7\" />\n      <line x1=\"21\" x2=\"9\" y1=\"12\" y2=\"12\" />\n    </svg>\n  );\n}\n\ninterface MenuItem {\n  label: string;\n  onClick?: () => void;\n  icon?: React.ReactNode;\n}\n\nexport interface ClerkUserButtonProps {\n  className?: string;\n  menuItems?: MenuItem[];\n}\n\nfunction getInitials(name: string): string {\n  return name\n    .split(\" \")\n    .map((part) => part[0])\n    .join(\"\")\n    .toUpperCase()\n    .slice(0, 2);\n}\n\nexport function ClerkUserButton({\n  className,\n  menuItems,\n}: ClerkUserButtonProps) {\n  const { isLoaded, isSignedIn, user } = useUser();\n  const { signOut } = useClerk();\n  const [open, setOpen] = React.useState(false);\n  const containerRef = React.useRef<HTMLDivElement>(null);\n\n  React.useEffect(() => {\n    if (!open) return;\n\n    function handleClickOutside(e: MouseEvent) {\n      if (\n        containerRef.current &&\n        !containerRef.current.contains(e.target as Node)\n      ) {\n        setOpen(false);\n      }\n    }\n\n    function handleEscape(e: KeyboardEvent) {\n      if (e.key === \"Escape\") {\n        setOpen(false);\n      }\n    }\n\n    document.addEventListener(\"mousedown\", handleClickOutside);\n    document.addEventListener(\"keydown\", handleEscape);\n    return () => {\n      document.removeEventListener(\"mousedown\", handleClickOutside);\n      document.removeEventListener(\"keydown\", handleEscape);\n    };\n  }, [open]);\n\n  if (!isLoaded) {\n    return (\n      <div\n        data-slot=\"clerk-user-button\"\n        className={cn(\"relative inline-block\", className)}\n      >\n        <div className=\"size-8 animate-pulse rounded-full bg-muted\" />\n      </div>\n    );\n  }\n\n  const name = user?.fullName || \"User\";\n  const email = user?.primaryEmailAddress?.emailAddress || \"\";\n  const imageUrl = user?.imageUrl;\n  const initials = isSignedIn ? getInitials(name) : \"?\";\n\n  const defaultItems: MenuItem[] = [\n    {\n      label: \"Profile\",\n      icon: <UserIcon className=\"size-4\" />,\n    },\n    {\n      label: \"Settings\",\n      icon: <SettingsIcon className=\"size-4\" />,\n    },\n  ];\n\n  const resolvedItems = menuItems ?? defaultItems;\n\n  return (\n    <div\n      data-slot=\"clerk-user-button\"\n      ref={containerRef}\n      className={cn(\"relative inline-block\", className)}\n    >\n      <button\n        data-slot=\"clerk-user-button-trigger\"\n        type=\"button\"\n        onClick={() => setOpen((prev) => !prev)}\n        className=\"flex items-center gap-2 rounded-full p-0.5 transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n      >\n        {imageUrl ? (\n          <img\n            data-slot=\"clerk-user-button-avatar\"\n            src={imageUrl}\n            alt={name}\n            className=\"size-8 rounded-full object-cover\"\n          />\n        ) : (\n          <span\n            data-slot=\"clerk-user-button-avatar\"\n            className=\"flex size-8 items-center justify-center rounded-full bg-primary text-xs font-medium text-primary-foreground\"\n          >\n            {initials}\n          </span>\n        )}\n      </button>\n\n      {open && (\n        <div\n          data-slot=\"clerk-user-button-menu\"\n          className=\"absolute right-0 top-full z-50 mt-2 w-64 overflow-hidden rounded-xl border border-border bg-card shadow-lg\"\n        >\n          <div\n            data-slot=\"clerk-user-button-menu-header\"\n            className=\"border-b border-border px-4 py-3\"\n          >\n            <div className=\"flex items-center gap-3\">\n              {imageUrl ? (\n                <img\n                  src={imageUrl}\n                  alt={name}\n                  className=\"size-10 rounded-full object-cover\"\n                />\n              ) : (\n                <span className=\"flex size-10 items-center justify-center rounded-full bg-primary text-sm font-medium text-primary-foreground\">\n                  {initials}\n                </span>\n              )}\n              <div className=\"min-w-0 flex-1\">\n                <p className=\"truncate text-sm font-semibold text-foreground\">\n                  {name}\n                </p>\n                <p className=\"truncate text-xs text-muted-foreground\">\n                  {email}\n                </p>\n              </div>\n            </div>\n          </div>\n\n          <div data-slot=\"clerk-user-button-menu-items\" className=\"py-1\">\n            {resolvedItems.map((item) => (\n              <button\n                key={item.label}\n                data-slot=\"clerk-user-button-menu-item\"\n                type=\"button\"\n                onClick={() => {\n                  item.onClick?.();\n                  setOpen(false);\n                }}\n                className=\"flex w-full items-center gap-2.5 px-4 py-2 text-sm text-foreground transition-colors hover:bg-accent\"\n              >\n                {item.icon && (\n                  <span className=\"text-muted-foreground\">{item.icon}</span>\n                )}\n                {item.label}\n              </button>\n            ))}\n          </div>\n\n          <div className=\"border-t border-border py-1\">\n            <button\n              data-slot=\"clerk-user-button-sign-out\"\n              type=\"button\"\n              onClick={() => {\n                signOut();\n                setOpen(false);\n              }}\n              className=\"flex w-full items-center gap-2.5 px-4 py-2 text-sm text-foreground transition-colors hover:bg-accent\"\n            >\n              <span className=\"text-muted-foreground\">\n                <LogOutIcon className=\"size-4\" />\n              </span>\n              Sign out\n            </button>\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "A polished user button with avatar (image or initials fallback) and dropdown menu. Customizable menu items and actions. Wire up with Clerk's useUser() and useClerk() hooks.",
  "categories": [
    "clerk"
  ],
  "type": "registry:block"
}