Avatar
A person's or store's photo with an initials fallback that needs no script, and AvatarGroup, a stack of avatars and a count announced as one item ("Maya, Jonah, Priya, and 4 more").
Import
import { Avatar, AvatarGroup } from '@nizivo/react/avatar';
// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css
import '@nizivo/react/css/avatar.css';Static or interactive
Server. It ships no client JavaScript when a Server Component renders it, in every Next.js build
mode; its code is bundled only when a client component (a 'use client' file) imports it. The
initials are always in the server HTML and the photo is laid over them with alt="", so in Chromium
and Firefox a photo that fails to load shows the initials with no onError handler and no hydration.
WebKit (Safari) draws its own broken-image mark over the middle of the initials, and no script-free
markup avoids it (D-bf); test/browser/avatar.spec.tsx samples the pixels in all three engines and
pins that. How to choose.
In a client app (a Vite or React Router single-page app, or a 'use client' file) it is an ordinary React component: event handlers, refs and state work as usual. In a client app.
Names
The avatar is role="img" named by name; the image inside it has alt="" and the initials are
aria-hidden. When the name is printed next to the avatar, pass decorative: the avatar is then
aria-hidden. Inside an AvatarGroup every avatar is decorative and the group is one role="img"
named by the list of names, joined with Intl.ListFormat in the group's locale.
A photo that fails to load in WebKit
The fallback to the initials needs no script in Chromium and Firefox. WebKit (Safari) is different:
when src fails to load it draws its own broken-image mark, a "?" glyph, over the middle of the
initials, and nothing script-free avoids it. No CSS can tell a broken image from a loaded one in
WebKit; an SVG <image> shows the mark in Chromium and WebKit too, and <object> is blocked by a
strict Content Security Policy's object-src (D-bf, kept by the maintainer's decision M42). So:
- pass
src(orimage) only when the photo is known to load, for example a URL your server has just checked, or one from your own storage; - when it may be gone, leave
srcout and the initials render, the same markup in every engine.
Examples
Group
Three faces and a count, read as one item: "Maya Rao, Jonah Okafor, Priya Shah, and 4 more".
import type { JSX } from 'react';
import { Avatar, AvatarGroup } from '@nizivo/react/avatar';
/** A stand-in portrait (an inline SVG), so the example needs no image file. */
const PHOTO =
'data:image/svg+xml,' +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" fill="#c9b8a6"/>' +
'<circle cx="32" cy="26" r="12" fill="#8a6f5a"/><rect x="12" y="42" width="40" height="30" rx="20" fill="#8a6f5a"/></svg>',
);
/** A photo, the initials fallback at every size, and a decorative avatar next to a printed name. */
export function AvatarExample(): JSX.Element {
return (
<div>
<Avatar name="Maya Rao" src={PHOTO} size="lg" />
<Avatar name="Jonah Okafor" size="sm" />
<Avatar name="Jonah Okafor" />
<Avatar name="Jonah Okafor" size="lg" />
<Avatar name="Jonah Okafor" size="xl" />
<p>
<Avatar name="Priya Shah" decorative /> Priya Shah
</p>
</div>
);
}
export function AvatarGroupExample(): JSX.Element {
return (
<AvatarGroup total={7}>
<Avatar name="Maya Rao" src={PHOTO} />
<Avatar name="Jonah Okafor" />
<Avatar name="Priya Shah" src={PHOTO} />
</AvatarGroup>
);
}
Props
Avatar
A person's or store's photo laid over their initials, with no script: the initials are always rendered, so a missing photo needs no onError. Known limit: a src that fails to load shows WebKit's (Safari's) broken-image mark over the initials, with no script-free fix; pass src only when it is known to load, or leave it out and the initials show.
Server component Renders data-slot="avatar" with the class nz-avatar. Props: AvatarProps. Source: src/avatar/avatar.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
decorative | boolean | false | The name is printed next to the avatar, so the avatar is hidden from assistive technology (Figma: |
image | ReactElement<{
className?: string | undefined;
}> | – | A framework image element in place of |
initials | string | – | Replaces the initials derived from |
namerequired | string | – | The person's or store's name. It is the avatar's accessible name (unless |
render | RenderProp<AvatarState> | – | Composition of the root |
size | AvatarSize"sm""md""lg""xl" | 'md' | |
sizes | string | – | |
src | string | – | The photo. Without it (or |
srcSet | string | – | Passed to the |
- Also takes every attribute of
<span>exceptchildren. (Omit<ComponentPropsWithRef<"span">, "children">)
render takes an element, or, in client code, a function of the props and this state (AvatarState):
| Name | Type | Default | Description |
|---|---|---|---|
decorativerequired | boolean | – | |
imagerequired | boolean | – | An image source or |
sizerequired | AvatarSize"sm""md""lg""xl" | – |
AvatarGroup
A stack of avatars and a count, announced as one item (Figma: "Maya, Jonah, Priya and 4 more"): the root is role="img" named by the list of names, and every avatar inside is decorative.
Server component Renders data-slot="avatar-group" with the class nz-avatar-group. Props: AvatarGroupProps. Source: src/avatar/avatar.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | – |
|
labels | Partial<AvatarGroupLabels> | – | |
locale | string | 'en-US' | BCP 47 tag for the count and the list of names. |
max | number | 3 | How many avatars to show before the count. Figma: up to 3. |
render | RenderProp<AvatarGroupState> | – | |
size | AvatarSize"sm""md""lg""xl" | 'md' | Applied to every avatar and the count. |
total | number | the number of avatars | Everyone in the group, when it is more than the avatars passed (the server sends three faces and a count). |
- Also takes every attribute of
<span>;childrenis redeclared above. (ComponentPropsWithRef<"span">)
render takes an element, or, in client code, a function of the props and this state (AvatarGroupState):
| Name | Type | Default | Description |
|---|---|---|---|
hiddenrequired | number | – | People not shown as an avatar. |
sizerequired | AvatarSize"sm""md""lg""xl" | – |
Other exports
AvatarGroupLabelstypeimport type { AvatarGroupLabels } from '@nizivo/react/avatar'AvatarGroup's own strings (§16.4): serialisable, English defaults.
{count}is replaced by the formatted count, and aPluralLabel({ one, few, many, other }, the locale's CLDR categories) picks its template by that count throughformatLabel.Name Type Default Description morerequiredstring | PluralLabel'{count} more'The count's words in the group's accessible name ("Maya, Jonah, Priya and 4 more").
overflowrequiredstring | PluralLabel'+{count}'The visible count after the shown avatars.
AvatarGroupPropstypeimport type { AvatarGroupProps } from '@nizivo/react/avatar'The props of
AvatarGroup, listed above.AvatarGroupStatetypeimport type { AvatarGroupState } from '@nizivo/react/avatar'The render state of
AvatarGroup, listed above.AvatarPropstypeimport type { AvatarProps } from '@nizivo/react/avatar'The props of
Avatar, listed above.AvatarSizetypeimport type { AvatarSize } from '@nizivo/react/avatar'Figma "Size": Sm 24, Md 32, Lg 40, XL 56 px.
type AvatarSize = "sm" | "md" | "lg" | "xl"AvatarStatetypeimport type { AvatarState } from '@nizivo/react/avatar'The render state of
Avatar, listed above.
Data attributes and slots
Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.
| Slot | Rendered by | Data attributes |
|---|---|---|
avatar.nz-avatar | Avatarroot (takes render) |
|
avatar-group.nz-avatar-group | AvatarGrouproot (takes render) |
|
avatar-group-more.nz-avatar-group-more | AvatarGroupinner element (CSS only) | none |
avatar-image.nz-avatar-image | Avatarinner element (CSS only) | none |
avatar-initials.nz-avatar-initials | Avatarinner element (CSS only) | none |
CSS hooks
Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.
| Hook | Default | Read as |
|---|---|---|
--nz-avatar-bg | --nz-bg-brand-muted |
|
--nz-avatar-fg | --nz-text-brand |
|
--nz-avatar-font |
|
|
--nz-avatar-group-more-bg | --nz-bg-muted |
|
--nz-avatar-group-more-fg | --nz-text-secondary |
|
--nz-avatar-group-ring | --nz-bg-surface |
|
--nz-avatar-radius | --nz-radius-full |
|
--nz-avatar-size |
|
|
Layers: nz.components. Classes: .nz-avatar, .nz-avatar-group, .nz-avatar-group-more, .nz-avatar-image, .nz-avatar-initials.
Tokens this stylesheet reads (18)
--nz-bg-brand-muted, --nz-bg-muted, --nz-bg-surface, --nz-border-width-focus, --nz-font-label-l, --nz-font-label-m, --nz-font-label-s, --nz-radius-full, --nz-space-10, --nz-space-12, --nz-space-2, --nz-space-6, --nz-space-8, --nz-text-brand, --nz-text-secondary, --nz-tracking-label-l, --nz-tracking-label-m, --nz-tracking-label-s
Accessibility
Nothing in this component takes focus or keyboard input.
Keyboard
None.
Figma
Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.
- Canonical: Figma node
7370:21, Figma node7370:26 - Ghost masters: none
AppShell
The page's frame. The Sidebar and the TopBar on screens 640 px and wider, the MobileTopBar and the MobileTabBar below that, around the page's main content, switched by CSS with no script.
Badge
A short status label with an optional decorative dot. The label carries the meaning, never the colour alone.