Skip to content
  1. Keyline UI
  2. Avatar
Keyline UIComponents
  • Get started
    • Installation
    • Theming
    • Modes
    • Configuration
    • Composition
  • Actions
    • Button
    • Button group
    • Split button
    • Toggle
    • Toggle group
    • Toolbar
    • Menu
    • Menubar
    • Context menu
  • Inputs
    • Field and form
    • Checkbox
    • Checkbox group
    • Radio group
    • Switch
    • Slider
    • Segmented
    • Select
    • Native select
    • Combobox
    • Multiselect
    • Number field
    • OTP field
    • Time field
    • Date picker and calendar
    • Date range
  • Data
    • Table
    • Chart
    • Stat and meter
    • Status and badge
    • Progress
    • Skeleton
    • Empty
    • Avatar
    • Kbd
    • Marker
  • Navigation
    • Tabs
    • Breadcrumb
    • Pagination
    • Nav
    • Navigation menu
    • Command menu
  • Overlays
    • Dialog
    • Alert dialog
    • Drawer
    • Sheet
    • Popover
    • Hover card
    • Tooltip
    • Toast
    • Notice
  • Layout
    • App shell
    • Sidebar
    • Panel
    • Resizable
    • Scroll area
    • Accordion
    • Collapsible
    • Carousel
    • Inspector
    • Item
    • Direction
  • Conversation
    • Message
    • Bubble
    • Message scroller
    • Attachment
    • Questionnaire
  • Blocks
    • Shipments

Avatar

A person or organisation as a square cell, never a circle. The photo fills it; while it loads or if it fails, the initials hold the cell. A group shares its seams and folds the overflow into one count cell.

Loading the live example…

"use client"

import { useState } from "react"
import { Avatar, AvatarGroup } from "@/components/keyline/ui/avatar"
import { CheckboxGroup } from "@/components/keyline/ui/checkbox-group"

const STAFF = [
  { value: "dwight", label: "Dwight Schrute", photo: "/avatars/dwight-schrute.svg" },
  { value: "jim", label: "Jim Halpert", photo: "/avatars/jim-halpert.svg" },
  { value: "phyllis", label: "Phyllis Vance", photo: "/avatars/phyllis-vance.svg" },
  { value: "stanley", label: "Stanley Hudson", photo: "/avatars/stanley-hudson.svg" },
  { value: "andy", label: "Andy Bernard", photo: "/avatars/andy-bernard.svg" },
  { value: "pam", label: "Pam Beesly", photo: "/avatars/pam-beesly.svg" },
]

export function AccountTeam() {
  const [team, setTeam] = useState(["dwight", "jim", "phyllis"])
  const people = STAFF.filter((s) => team.includes(s.value))

  return (
    <div className="grid gap-5 sm:grid-cols-[1fr_auto] sm:items-start">
      <CheckboxGroup legend="Lackawanna County account" description="Who gets the county's orders and calls." options={STAFF} value={team} onValueChange={setTeam} />
      <div className="grid gap-2 sm:justify-items-end">
        <span className="text-[13px] font-semibold text-kl-ink-2">On the account</span>
        {people.length ? (
          <AvatarGroup label="On the Lackawanna County account" max={4}>
            {people.map((p) => (
              <Avatar key={p.value} name={p.label} src={p.photo} />
            ))}
          </AvatarGroup>
        ) : (
          <span className="text-[14px] text-kl-ink-3">Nobody yet</span>
        )}
      </div>
    </div>
  )
}

Installation

npx shadcn@latest add @keyline-ui/avatar

Run it where your components.json is. The first time, map the @keyline-ui registry: one command, in Installation.

Usage

import { Avatar, AvatarGroup } from "@/components/keyline/ui/avatar"
<AvatarGroup label="On the account" max={4}>
  <Avatar name="Dwight Schrute" />
  <Avatar name="Jim Halpert" src={jim.photo} />
</AvatarGroup>

Examples

Status and a missing photo

status turns the avatar's floor into a line with its own shape for each kind, and joins the words to its name. Toby's photo fails to load, so his initials show.

Loading the live example…

"use client"

import { Avatar, type AvatarStatus } from "@/components/keyline/ui/avatar"

const PEOPLE: { name: string; role: string; status: AvatarStatus; where: string; src?: string }[] = [
  { name: "Michael Scott", role: "Regional Manager", status: "normal", where: "In his office", src: "/avatars/michael-scott.svg" },
  { name: "Jim Halpert", role: "Sales", status: "waiting", where: "At a client, back at 2", src: "/avatars/jim-halpert.svg" },
  { name: "Dwight Schrute", role: "Assistant to the Regional Manager", status: "attention", where: "Three missed calls from corporate", src: "/avatars/dwight-schrute.svg" },
  // this photo is damaged on purpose: the initials hold the cell and no broken image shows
  { name: "Toby Flenderson", role: "Human Resources", status: "none", where: "Out today", src: "data:image/jpeg;base64,AAAA" },
]

export function WhosIn() {
  return (
    <ul className="m-0 grid list-none gap-3 p-0">
      {PEOPLE.map((p) => (
        <li key={p.name} className="grid grid-cols-[auto_1fr] items-center gap-3">
          <Avatar name={p.name} src={p.src} status={p.status} size="lg" />
          <div className="grid min-w-0 gap-0.5 text-[14px]">
            <span className="font-semibold">{p.name}</span>
            <span className="text-kl-ink-2">
              {p.role} · {p.where}
            </span>
          </div>
        </li>
      ))}
    </ul>
  )
}

Props

PropTypeDefaultDescription
namestringDefault —Names the avatar and gives the initials.
srcstringDefault —Photo or logo. The initials show until it loads, and if it fails.
size"sm" | "md" | "lg" | "xl"Default "md"24, 32, 40 or 56px.
status"normal" | "waiting" | "attention" | "done" | "none"Default —Active, away, needs attention, done or offline.
statusLabelstringDefault by kindThe words for the status.
decorativebooleanDefault falseThe name is printed beside it: hide the avatar from screen readers.
fallbackReactNodeDefault initialsSomething other than the initials, such as an org code.
maxnumberDefault —On AvatarGroup: show at most this many cells; the rest become a count.
totalnumberDefault —On AvatarGroup: everyone, when more exist than you pass.

For a count that opens the full list, leave out max and put AvatarGroupCount in a popover trigger. avatarInitials(name) is exported too. Built on Base UI Avatar.

Sheet 39 of 73PreviousEmptyNextKbd
On this page