Browse docs
Browse docs
Show a user. Fall back gracefully when there's no picture.
import { Avatar } from '@dashforge/tw';
<Avatar src="/avatars/jane.png" alt="Jane Doe" /><Avatar src="/u/jane.png" alt="Jane Doe" /> {/* image */}
<Avatar name="Jane Doe" /> {/* initials: "JD" */}
<Avatar name="Jane Doe" fallbackIcon={<User />} /> {/* custom fallback */}
<Avatar /> {/* generic SVG */}Avatar resolves in this order, falling back on each failure:
src (image) — if it loadsname → auto-generated initials (first letter of the first 2 whitespace-separated words, uppercased)fallbackIcon (custom ReactNode)import { Avatar, Stack } from '@dashforge/tw';
import { Building } from 'lucide-react';
<Stack direction="row" gap={3} wrap align="center">
<Avatar src="/avatars/jane.png" alt="Jane Doe" name="Jane Doe" /> {/* step 1: src loads */}
<Avatar src="/avatars/missing.png" alt="Jane Doe" name="Jane Doe" /> {/* step 2: initials "JD" */}
<Avatar fallbackIcon={<Building size={18} />} /> {/* step 3: custom icon */}
<Avatar /> {/* step 4: generic SVG */}
</Stack><Avatar name="Jane" shape="circle" /> {/* default */}
<Avatar name="Jane" shape="rounded" /> {/* soft corners */}
<Avatar name="Jane" shape="square" /> {/* hard corners */}Override with explicit radius (Box-style token) for custom shapes:
<Avatar name="Jane" shape="rounded" radius="lg" />Five-step scale:
import { Avatar, Stack } from '@dashforge/tw';
<Stack direction="row" gap={3} align="center">
<Avatar name="Jane Doe" size="xs" color="primary" />
<Avatar name="Jane Doe" size="sm" color="primary" />
<Avatar name="Jane Doe" size="md" color="primary" />
<Avatar name="Jane Doe" size="lg" color="primary" />
<Avatar name="Jane Doe" size="xl" color="primary" />
</Stack>For the no-image case (initials/icon), color and tone resolve to the Dashforge token palette — fully type-safe:
<Avatar name="Jane" color="primary" tone={500} />
<Avatar name="Jane" color="success" tone={600} />
<Avatar name="Jane" color="danger" tone={500} />
<Avatar name="Jane" color="neutral" tone={300} />color is constrained to the palette keys (primary, secondary, success, ...). tone is constrained to the scale steps (50, 100, 200, ..., 900). Invalid combinations fail to compile.
For colors outside the palette: fall back to sx / className override (escape hatch).
onError handlerCustom fallback on image load failure:
<Avatar
src="/u/maybe.png"
alt="Maybe Jane"
name="Maybe Jane"
onError={(e) => analytics.track('avatar.load.error', { src: e.currentTarget.src })}
/>The default onError swaps to step 2 (initials) automatically — your handler runs in addition, not instead.
<AvatarGroup> wraps Avatar children, applies the same size to all, and shows a +N indicator when the count exceeds max.
import { Avatar, AvatarGroup } from '@dashforge/tw';
<AvatarGroup max={3}>
<Avatar name="Alice Smith" color="primary" />
<Avatar name="Bob Lee" color="success" />
<Avatar name="Carla Vega" color="warning" />
<Avatar name="David Park" color="info" />
<Avatar name="Erin Kim" color="danger" />
</AvatarGroup>
{/* Renders: A · B · C · +2 */}
<AvatarGroup size="lg" max={4}>
{/* size="lg" propagates to all children via cloneElement */}
<Avatar name="Alice" /> <Avatar name="Bob" /> <Avatar name="Carla" /> <Avatar name="David" />
</AvatarGroup>withRingBy default each avatar in the group gets a ring-neutral-50 ring — a subtle separator. Disable for a flush stack:
<AvatarGroup withRing={false} max={4}>
{/* ... */}
</AvatarGroup>The ring uses ring-neutral-50 (NOT dark:ring-neutral-100) because the neutral palette auto-inverts via the CSS var swap. Same pattern as the rest of the catalog — see Design decisions.
<Avatar name="Jane Doe"
access={{ requires: 'workspace.admin', when: 'denied:hide' }}
/>
<Avatar src="/u/me.png"
visibleWhen={(engine) => engine.getValue('showAuthor') === true}
/>Configure <Avatar> defaults application-wide.
import { patchTheme } from '@dashforge/tw-theme';
patchTheme({
components: {
Avatar: {
defaults: { shape: 'circle', size: 'md', color: 'neutral' },
},
},
});Configurable axes (AvatarVariantProps):
| Axis | Type | Notes |
|---|---|---|
shape | 'circle' | 'rounded' | 'square' | Border-radius semantic. |
radius | Box radius scale | Fine-grained override of shape. |
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | Diameter tier. |
color | intent role | Fallback background color when no src. |
tone | 50-950 | Fallback background shade within the color palette. |
Non-visual axes (src, alt, name, fallbackIcon, onError, access, visibleWhen) are not theme-configurable — content and behavior are per-instance.
Precedence chain (lowest → highest):
shape='circle', size='md', color='neutral', tone=200).theme.components.Avatar.defaults (application-wide).<Avatar shape="square" />) — wins over theme.sx — appended to the root and wins over conflicting classes via tailwind-merge.TypeScript: theme.components.Avatar.defaults autocompletes to the five axes above.
Reactivity: useComponentDefaults('Avatar') subscribes to the theme store — patchTheme re-renders every mounted instance inheriting the changed axis.
<Avatar> props| Prop | Type | Default | Description |
|---|---|---|---|
alt | string | — | alt text for the image. Required when src is set (a11y — screen readers need a name). When src is absent, the alt is implied from name. |
className | string | — | Standard React className — appended to the root via cn(). |
color | AvatarColor | 'neutral' | Intent color used for the fallback background. Ignored when the image loads successfully. |
fallbackIcon | ReactNode | — | Optional custom fallback ReactNode — replaces both initials and the generic icon when the image is absent / fails. Useful for Avatar-with-emoji or Avatar-with-custom-icon patterns. When passed, name is ignored for initials generation but kept as alt semantic.NB: this is the ONE exception to the "no fallback prop — only name" rule from the spec. Kept narrow because emoji / custom icon avatars are common enough that forcing sx for them would be friction. |
imgProps | ImgHTMLAttributes<HTMLImageElement> | — | Additional <img> HTML attributes (sizes, loading, srcSet, …). |
name | string | — | Person / entity name. Auto-generates initials when src is absent or the image fails to load. Algorithm: take the first letter of the first two whitespace-separated words, uppercased. "Maya Rodriguez" → "MR". "Cher" → "C". Empty → generic icon. |
radius | 'md' | 'sm' | 'lg' | 'none' | 'xl' | '2xl' | 'full' | — | Border radius — same enum as <Box rounded> (Box token scale). When set, overrides the shape mapping. Useful for matching a specific Card/Box radius (e.g., radius='2xl'). |
shape | AvatarShape | 'circle' | Shape — semantic shortcut. Override with radius for fine control on the Box token scale. |
size | AvatarSize | 'md' | Diameter tier — xs (20px) → xl (64px). Propagates to nested initials + fallback icon. |
src | string | — | Image URL. Falls back to initials/icon on load failure. |
sx | ClassValue | — | Root-element class shortcut (string or clsx-compatible value). |
tone | AvatarTone | — | Specific shade within the color palette (TS-safe). When set, overrides the default "soft" treatment. See AvatarTone for the full scale. |
<AvatarGroup> props| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Avatar children. |
className | string | — | Standard className. |
max | number | 4 | Max number of avatars to render visibly. Excess collapses into a trailing "+N" overflow indicator. |
size | AvatarSize | 'md' | Size applied to every child Avatar (unless the child explicitly overrides). Propagated via React.cloneElement. |
spacing | 'xs' | 'sm' | 'md' | 'sm' | Negative margin between overlapping avatars. Tighter = more overlap. Looser = more space. |
sx | ClassValue | — | sx escape hatch. |
withRing | boolean | true | Wrap each child avatar with a ring-2 ring-white halo for separation on busy backgrounds. |
<Avatar> is a small compound component with 3 named slots:
| Slot | Purpose |
|---|---|
root | Outer element carrying shape/size/color classes. Wraps either the <img>, the initials, or the fallback icon. |
initials | The <span> shown when no src is set (or the image fails) and name is present. |
icon | The generic fallback SVG or user-supplied fallbackIcon node. |
The public API doesn't expose a slotProps prop directly — the slot classes are consumed internally by the avatarVariants recipe. For per-slot overrides, use sx on the root and target inner elements via child-selectors (e.g. sx="[&>span]:font-black"). For richer per-slot control, use the individual utility classes on the elements you compose inside via fallbackIcon.
name, uppercased. "Jane Doe" → "JD". "Madonna" → "M". " jane van der meer " → "JV".<img> renders, it has alt="" (decorative) — the parent <span> carries the accessible name via aria-label. Avoids double-announce in screen readers.color and tone are constrained at the type level to the tokens declared in @dashforge/tw-tokens. Adding a new palette key in the tokens package immediately surfaces as a new valid color value here.sx / className override on the no-image background. The constraint is on color/tone props only — the className escape hatch is unrestricted.