User menu
Your account, settings, theme, and sign out behind the avatar. Opens as a bottom sheet on phones.
pnpm dlx shadcn@latest add @uiarc/user-menu- The single account entry point in an app top bar.
- Menus that combine presence status, theme preference, account links, and sign out.
- Apps that want async sign out with a spinner in the menu.
- Use dropdown-menu for generic command lists.
- Use theme-switch for a standalone light and dark toggle.
- Use workspace-sidebar when account and workspace switching live in a side navigation.
Installation
Add User menu with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/user-menuAdds the component and its local dependencies, and installs motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:
import { UserMenu } from "@/registry/components/user-menu/user-menu";import { CreditCard, Settings, UserRound } from "lucide-react"; export function TopBarAccount({ user }: { user: { name: string; email: string } }) { return ( <UserMenu user={{ ...user, plan: "Pro" }} showName onThemeChange={setThemePreference} items={[ { label: "Profile", icon: <UserRound size={16} />, onSelect: openProfile }, { label: "Settings", icon: <Settings size={16} />, keys: ["⌘", ","], onSelect: openSettings }, { label: "Billing", icon: <CreditCard size={16} />, onSelect: openBilling }, ]} onSignOut={() => signOut()} /> );}API reference
2 parts. The first is the root.
UserMenu
Account menu behind an avatar trigger: a compact identity header, a short group of account links, an inline theme switch, and sign out. Below 640px it opens as a bottom sheet.
userRequiredUserMenuUser–{ name, email, plan?, avatarSrc?, avatarSrcSet? }. Falls back to initials without a photo. plan shows as a small accent badge.itemsUserMenuItem[][]Account destinations: { label, icon?, keys?, onSelect? }. Keep it to three or four. keys show quiet shortcut hints such as ["⌘", ","].showNamebooleanfalseShows the name and a chevron beside the avatar from 640px up. Phones always show the avatar alone.theme"light" | "dark" | "system"–Controlled theme preference.defaultTheme"light" | "dark" | "system""system"Initial theme when uncontrolled.onThemeChange(theme: ThemePreference) => void–Reports the choice. Applying it to the page is up to the app. The menu stays open.showThemebooleantrueShows the inline light, dark, and system switch.status"available" | "busy" | "away"–Controlled presence status. Passing status or onStatusChange adds the presence dot and an inline status switch.defaultStatus"available" | "busy" | "away""available"Initial status when uncontrolled.onStatusChange(status: UserStatus) => void–Called when a status is chosen. The menu stays open.onSignOut() => void | Promise<unknown>–Sign out handler. A returned promise keeps the menu open with a spinner until it settles.signOutKeysstring[]–Shortcut hint for the sign out item.openboolean–Controlled open state.defaultOpenbooleanfalseInitial open state when uncontrolled. A menu that starts open does not take focus.onOpenChange(open: boolean) => void–Called when the menu opens or closes.align"start" | "center" | "end""end"Panel alignment against the trigger.portalbooleantrueRenders the desktop panel on the body. Pass false to keep it inside the trigger wrapper, for example in a contained preview. The mobile sheet always uses the body.refRef<HTMLButtonElement>–The trigger button, for returning focus.classNamestring–Extra class on the trigger.PresenceDot
The status mark on its own, for avatars elsewhere in the app. Each status has its own shape as well as color.
statusRequired"available" | "busy" | "away" | "offline"–Status to show.classNamestring–Extra class for positioning.- EnterorSpaceorArrowDown
- Opens the menu from the avatar and moves to the first row.
- ArrowUp
- Opens the menu from the avatar and moves to the last row.
- ArrowDownorArrowUp
- Moves between rows, looping at the ends. The highlight follows.
- HomeorEnd
- Moves to the first or last row.
- A to Z
- Typeahead jumps to the next row starting with the typed letters.
- ArrowLeftorArrowRight
- Chooses the previous or next option in the theme or status switch.
- EnterorSpace
- Activates the row, or chooses a theme or status without closing.
- EscapeorTab
- Closes the menu and returns focus to the avatar.
- The trigger has aria-haspopup="menu" and aria-expanded, and is labelled "Account menu, <name>" plus the status when one is shown.
- Rows are menuitem buttons; the theme and status switches are labelled groups of menuitemradio buttons with aria-checked.
- No focus rings are drawn: the highlight marks the keyboard position. Outside press, the scrim, and swipe down close the menu.
- Sign out sets aria-busy while an async handler runs; shortcut hints are aria-hidden.
- The panel scales and fades out of the avatar on a spring that never overshoots; rows fade in with a short stagger, and closing is a quick fade.
- One highlight glides between rows for the pointer and the keyboard; the switch thumb slides between options.
- On phones the sheet rises from the bottom edge and follows a downward drag, closing past a short distance or a quick flick.
- Reduced motion opens and closes with a short fade, jumps the highlight, and turns off dragging.
- The menu is 17.5rem wide, capped at the viewport width minus 24px, with 12px collision padding.
- Long names and emails ellipsize instead of widening the menu.
- The trigger is a fixed 40px avatar, and the theme row keeps a 44px minimum height for touch.
- Content renders only while open; each opening gets a fresh layout namespace so shared indicators do not animate from stale positions.
- The avatar image decodes async and falls back to initials on error.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use as the single account entry point in an app top bar. For generic command lists use dropdown-menu; for a standalone light/dark toggle use theme-switch.
- Keep items to three or four account destinations; the theme switch and sign out are built in.
- onThemeChange only reports; apply the theme to the document yourself, optionally with a view transition.
- Return the sign out promise so the item shows progress and the menu closes when it settles.
The full library index for assistants is at /llms.txt.