# Defaults: UserMenu (/themes/sdk/reference/chrome/defaults-4)

```ts
import { … } from '@poltura/theme-sdk/chrome';
```

## `UserMenu` [#usermenu]

The account button and its menu: sign in and register for guests; the account, the orders and sign out once signed in.

```ts
UserMenu(props: UserMenuProps): ReactElement
```

**Props:** [`UserMenuProps`](/themes/sdk/reference/chrome/types#usermenuprops)

<Tab value='Example'>

```tsx
import { UserMenu } from '@poltura/theme-sdk/chrome';

export function Account() {
  return <UserMenu display='label' greet />;
}
```

</Tab>
<Tab value='Source'>

```tsx title="source/user-menu.tsx"
import { useEffect, useRef, useState, type ReactElement } from 'react';
import { LogOut, Package, User } from 'lucide-react';
import type { UserMenuProps } from '@poltura/theme-sdk/chrome';
import { useCustomer } from '@poltura/theme-sdk/customer';
import { useTranslations } from '@poltura/theme-sdk/i18n';
import { Link } from '@poltura/theme-sdk/routing';
import { useFeatures } from '@poltura/theme-sdk/store';
import {
  Button,
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
  cn,
} from '@poltura/theme-sdk/ui';

/** The label glides to its measured width, so the name settling in never jumps the icon. */
function AnimatedLabel({
  pending,
  placeholder,
  text,
}: {
  pending: boolean;
  placeholder: string;
  text: string;
}): ReactElement {
  const measureRef = useRef<HTMLSpanElement>(null);
  const [width, setWidth] = useState<number | null>(null);

  useEffect(() => {
    const element = measureRef.current;
    if (!element) return;
    // Hidden below a breakpoint the label measures 0: never lock that in, measure again once shown.
    const measure = () => {
      const measured = element.offsetWidth;
      setWidth(measured > 0 ? measured : null);
    };
    let frame: number | null = requestAnimationFrame(() => {
      frame = null;
      measure();
    });
    const observer = new ResizeObserver(measure);
    observer.observe(element);
    return () => {
      if (frame !== null) cancelAnimationFrame(frame);
      observer.disconnect();
    };
  }, [pending, text]);

  return (
    <span
      className='flex overflow-hidden whitespace-nowrap transition-[width] duration-300 ease-out'
      style={width === null ? undefined : { width }}
    >
      <span ref={measureRef} className='block whitespace-nowrap'>
        {pending ? (
          <span className='relative block'>
            <span className='invisible'>{placeholder}</span>
            <span
              aria-hidden
              className='absolute inset-x-0 top-1/2 h-3 -translate-y-1/2 rounded-sm bg-muted-foreground/20 animate-pulse'
            />
          </span>
        ) : (
          <span key={text} className='block animate-in slide-in-from-bottom-1 duration-300'>
            {text}
          </span>
        )}
      </span>
    </span>
  );
}

const MENU_LINK =
  'flex items-center gap-4 p-3 hover:bg-muted/50 transition-all rounded-lg relative group border border-transparent hover:border-border/50 w-full cursor-pointer';
const MENU_LINK_ICON =
  'w-9 h-9 rounded-md bg-card flex items-center justify-center border border-border/20 group-hover:border-primary/20 transition-colors';
const MENU_ITEM = 'p-0 focus:bg-transparent focus:outline-none';
const PANEL_HEAD =
  'p-5 border-b border-border/50 flex items-center justify-between bg-gradient-to-r from-muted/20 to-transparent';
const AVATAR =
  'w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center border border-primary/20';

export function UserMenu({
  display = 'icon',
  greet = false,
  className,
}: UserMenuProps): ReactElement {
  const { customer, status, signOut } = useCustomer();
  const t = useTranslations('userDropdown');
  const [signingOut, setSigningOut] = useState(false);
  const features = useFeatures();
  if (!features.customerAccounts) return <></>;
  const displayName = customer?.givenName || t('account');
  const triggerName = greet ? displayName : t('account');
  const accessibleName = customer?.givenName
    ? t('accountNamed', { name: customer.givenName })
    : t('account');

  const handleSignOut = async () => {
    setSigningOut(true);
    try {
      await signOut();
    } finally {
      setSigningOut(false);
    }
  };

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <button
          aria-label={accessibleName}
          data-theme-control='account'
          className={cn(
            'user-menu-trigger tap-area relative flex shrink-0 items-center gap-2 px-3 py-2 rounded-lg transition-all duration-300 group cursor-pointer border border-transparent',
            'focus-ring',
            className,
          )}
        >
          <div className='relative flex'>
            <User
              aria-hidden
              className='h-5 w-5 text-current group-hover:text-primary transition-colors duration-300 group-data-[state=open]:text-primary'
            />
          </div>
          {display === 'label' ? (
            <span className='user-menu-label flex text-sm font-bold tracking-wide uppercase transition-colors duration-300 text-current group-hover:text-primary group-data-[state=open]:text-primary'>
              <AnimatedLabel
                pending={status === 'loading'}
                placeholder={t('account')}
                text={triggerName}
              />
            </span>
          ) : null}
        </button>
      </DropdownMenuTrigger>

      <DropdownMenuContent
        className='user-menu-content w-72 bg-popover border-border/50 text-foreground p-0 shadow-[var(--overlay-shadow)] mt-2 overflow-hidden animate-in fade-in zoom-in-95 slide-in-from-top-2 duration-200 ring-1 ring-border'
        align='end'
        sideOffset={8}
      >
        {customer ? (
          <>
            <div className={PANEL_HEAD}>
              <div className='flex items-center gap-3'>
                <div className={AVATAR}>
                  <User className='h-5 w-5 text-primary' />
                </div>
                <div className='overflow-hidden'>
                  <h3 className='user-menu-label text-sm font-bold uppercase tracking-wider text-foreground leading-tight truncate max-w-[180px]'>
                    {displayName}
                  </h3>
                  <p className='user-menu-label text-[10px] text-muted-foreground font-medium uppercase tracking-widest truncate max-w-[180px]'>
                    {customer.email}
                  </p>
                </div>
              </div>
            </div>

            <div className='p-2 space-y-1'>
              <DropdownMenuItem asChild className={MENU_ITEM}>
                <Link href='/profile' className={MENU_LINK}>
                  <div className={MENU_LINK_ICON}>
                    <User className='h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors' />
                  </div>
                  <div>
                    <span className='text-sm font-bold text-foreground group-hover:text-primary transition-colors block'>
                      {t('myProfile')}
                    </span>
                    <p className='text-[11px] text-muted-foreground group-hover:text-muted-foreground/80'>
                      {t('personalData')}
                    </p>
                  </div>
                </Link>
              </DropdownMenuItem>

              <DropdownMenuItem asChild className={MENU_ITEM}>
                <Link href='/profile/orders' className={MENU_LINK}>
                  <div className={MENU_LINK_ICON}>
                    <Package className='h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors' />
                  </div>
                  <div>
                    <span className='text-sm font-bold text-foreground group-hover:text-primary transition-colors block'>
                      {t('myOrders')}
                    </span>
                    <p className='text-[11px] text-muted-foreground group-hover:text-muted-foreground/80'>
                      {t('previousPurchases')}
                    </p>
                  </div>
                </Link>
              </DropdownMenuItem>
            </div>

            <div className='p-2 border-t border-border/50 bg-muted/20'>
              <DropdownMenuItem
                onSelect={() => void handleSignOut()}
                disabled={signingOut}
                className='w-full flex items-center justify-center p-0 focus:bg-transparent focus:outline-none cursor-pointer'
              >
                <div className='user-menu-label w-full flex items-center justify-center gap-2 bg-destructive/10 hover:bg-destructive/20 border border-destructive/20 hover:border-destructive/30 text-destructive hover:text-destructive uppercase font-bold text-xs tracking-wider h-10 rounded-lg transition-all duration-300'>
                  <LogOut className='h-3.5 w-3.5' />
                  <span>{signingOut ? t('signingOut') : t('signOut')}</span>
                </div>
              </DropdownMenuItem>
            </div>
          </>
        ) : (
          <>
            <div className={PANEL_HEAD}>
              <div className='flex items-center gap-3'>
                <div className={AVATAR}>
                  <User className='h-5 w-5 text-primary' />
                </div>
                <div>
                  <h3 className='user-menu-label text-sm font-bold uppercase tracking-wider text-foreground leading-tight'>
                    {t('account')}
                  </h3>
                  <p className='user-menu-label text-[10px] text-muted-foreground font-medium uppercase tracking-widest'>
                    {t('loginRequired')}
                  </p>
                </div>
              </div>
            </div>

            <div className='p-6'>
              <div className='mb-6 text-center'>
                <div className='w-16 h-16 rounded-lg bg-primary/10 flex items-center justify-center mx-auto mb-4 border border-primary/20'>
                  <User className='h-8 w-8 text-primary' />
                </div>
                <h4 className='user-menu-label text-sm font-bold text-foreground mb-1.5 uppercase tracking-wide'>
                  {t('welcome')}
                </h4>
                <p className='text-xs text-muted-foreground leading-relaxed max-w-[200px] mx-auto'>
                  {t('loginDescription')}
                </p>
              </div>

              <div className='grid gap-3'>
                <DropdownMenuItem asChild className={MENU_ITEM}>
                  <Link href='/auth/login' className='w-full cursor-pointer'>
                    <Button className='user-menu-label w-full uppercase font-bold text-xs tracking-wider h-11'>
                      {t('login')}
                    </Button>
                  </Link>
                </DropdownMenuItem>

                {features.registration ? (
                  <DropdownMenuItem asChild className={MENU_ITEM}>
                    <Link href='/auth/register' className='contents'>
                      <Button
                        variant='outline'
                        className='user-menu-label w-full uppercase font-bold text-xs tracking-wider h-11 border-border/50'
                      >
                        {t('register')}
                      </Button>
                    </Link>
                  </DropdownMenuItem>
                ) : null}
              </div>
            </div>
          </>
        )}
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
```

</Tab>


**See also:** [`useHeader`](/themes/sdk/reference/chrome/hooks#useheader)
