Lumen Docs

Avatar

User avatar primitive. Located at apps/web/components/ui/avatar.tsx.

Usage

<Avatar name="Gani Sigit" size="md" color="#93a4c4" />

Renders a circular avatar with initials (first letters of first + last word of name). Color is the background.

Props

  • name — required. Used for initials and aria-label.
  • sizesm (24px) | md (32px) | lg (48px). Default md.
  • color — optional background color. Default: derived from name hash.
  • src — optional image URL. Falls back to initials on error.
  • className — additional classes.

Examples

Initial-based (default)

<Avatar name="Ari Rachman" size="sm" />

Background color auto-derived from a hash of name. Ensures the same user always gets the same color across sessions.

With image

<Avatar
  name="Gani Sigit"
  size="lg"
  src="https://.../avatar.jpg"
/>

Explicit color

<Avatar name="Gani" color="#93a4c4" />

Use user.avatarColor from the DB:

<Avatar name={user.name} color={user.avatarColor || undefined} />

Stacked

<div className="flex items-center">
  {members.slice(0, 4).map((m, i) => (
    <Avatar
      key={m.id}
      name={m.name}
      color={m.avatarColor}
      className={i > 0 ? "-ml-2" : ""}
    />
  ))}
  {members.length > 4 && (
    <div className="w-8 h-8 rounded-full bg-surface-2 text-text-muted text-xs font-medium flex items-center justify-center -ml-2">
      +{members.length - 4}
    </div>
  )}
</div>

Initials logic

function initials(name: string): string {
  const words = name.trim().split(/\s+/);
  if (words.length === 1) return words[0].slice(0, 2).toUpperCase();
  return (words[0][0] + words[words.length - 1][0]).toUpperCase();
}
  • "Gani Sigit" → "GS"
  • "John" → "JO"
  • "Gani M Sigit" → "GS" (first + last, middle ignored)

Don't

  • Don't use as a clickable action — wrap in a Button or add click handler on the parent
  • Don't skip the name prop — used for screen reader aria-label
  • Don't set size to arbitrary values via className — use the size prop (ensures consistent ratio)