Avatar
The <smart-avatar> component renders a user/entity avatar in either single or grouped form. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract AvatarBaseComponent defines the shared API — imageUrl, initials, size, shape, notificationPosition, group (for stacked/multi-avatar display), optional IAvatarOptions, cssClass (alias class), and a derived isGroup computed signal that is true when group() has at least one item. AvatarStandardComponent is a barebones placeholder concrete implementation. AvatarComponent is the public wrapper that renders AvatarStandardComponent by default and accepts a custom replacement via AVATAR_STANDARD_COMPONENT_TOKEN.
Usage
<smart-avatar [initials]="initials" [size]="size" [shape]="shape" />
<smart-avatar [group]="team" [options]="groupOptions" size="sm" />
Components
AvatarComponent (<smart-avatar>)
Main wrapper component. Renders AvatarStandardComponent by default. When AVATAR_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet.
AvatarStandardComponent (<smart-avatar-standard>)
Barebones placeholder concrete implementation. Renders a host <span> carrying the external cssClass plus data-size and data-shape attributes. Inside:
- Group mode (
isGroup()istrue): one<span class="smart-avatar-group-item">perIAvatarItem(tracked byid). Each item renders an<img>when the item hasimageUrl, otherwise a<span class="smart-avatar-initials">with the item'sinitials. - Single mode (
isGroup()isfalse): an<img>whenimageUrl()is set, otherwise<span class="smart-avatar-initials">wheninitials()is set, otherwise a<span aria-hidden="true" class="smart-avatar-placeholder">·</span>fallback.
It does not include Tailwind UI styling — it exists solely as the default structural placeholder until a custom implementation is registered through the token.
AvatarPresetComponent (<smart-avatar-preset>)
Styled variation that extends AvatarBaseComponent and is a drop-in replacement for AvatarStandardComponent. Register it via AVATAR_STANDARD_COMPONENT_TOKEN to restyle every <smart-avatar>, or use the <smart-avatar-preset> selector directly. It renders three content modes — an <img> (when imageUrl is set), an initials chip (when initials is set or options.placeholderType === 'initials'), or an SVG icon placeholder (the default) — across the full SmartAvatarSize scale (xs→size-8, sm→size-9.5, md→size-11, lg→size-15.5, xl→size-20) in both circle (rounded-full) and rounded (rounded-lg) shapes. When notificationPosition (top/bottom) is set the avatar is wrapped in a relative container with a corner status dot; when group() is non-empty it renders an overlapping stacked group (negative -space-x-2, ringed members), reversed when options.stackDirection === 'bottom-to-top'. All classes are smart:-prefixed Tailwind with explicit dark: variants. The class recipes live in preset/preset-classes.util.ts (getImageClasses, getInitialsClasses, getIconWrapperClasses, getStatusClasses, getGroupContainerClasses, getGroupItemImageClasses, getGroupItemInitialsClasses).
Because
AvatarComponentrenders injected components viaNgComponentOutlet(which passes inputs by canonical name),AvatarPresetComponentoverridescssClassasinput<string>('')without theclassalias. Bind it as[cssClass]when using the<smart-avatar-preset>selector directly, or just passclasson<smart-avatar>(the wrapper forwards it).
AvatarBaseComponent (abstract)
Abstract base directive for extending custom avatar implementations. Static smartType: DynamicComponentType = 'avatar'. Exposes:
imageUrl: InputSignal<string | undefined>initials: InputSignal<string | undefined>size: InputSignal<SmartAvatarSize>(default'md')shape: InputSignal<SmartAvatarShape>(default'circle')notificationPosition: InputSignal<'top' | 'bottom' | undefined>group: InputSignal<IAvatarItem[] | undefined>options: InputSignal<IAvatarOptions | undefined>cssClass: InputSignal<string>(aliasclass, default'')isGroup = computed(() => !!this.group()?.length)
API
Inputs
| Input | Type | Default | Description |
|---|---|---|---|
imageUrl | InputSignal<string | undefined> | - | URL of the avatar image (single mode) |
initials | InputSignal<string | undefined> | - | Initials shown when no imageUrl is provided (single mode) |
size | InputSignal<SmartAvatarSize> | 'md' | Avatar size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' |
shape | InputSignal<SmartAvatarShape> | 'circle' | Avatar shape: 'circle' | 'rounded' |
notificationPosition | InputSignal<'top' | 'bottom' | undefined> | - | Reserved for custom implementations to position a notification dot/badge |
group | InputSignal<IAvatarItem[] | undefined> | - | When set, renders a group of avatars instead of a single one |
options | InputSignal<IAvatarOptions | undefined> | - | Optional configuration (placeholder type, stack direction) |
class | InputSignal<string> | '' | External CSS classes (alias for cssClass) |
IAvatarItem
Each item in a group must have a unique id (used as the @for track key). When imageUrl is present the item renders as an <img>; otherwise as initials.
IAvatarOptions
placeholderType(default'icon') — the fallback when there is noimageUrl. Both variants follow the same rule:initialsare shown whenever they are set. Without initials,AvatarPresetComponentrenders an SVG icon ('icon') or an empty initials chip ('initials'), whileAvatarStandardComponentrenders a·placeholder. The standard also exposes the value asdata-placeholder-typeon its root<span>.stackDirection(default'top-to-bottom') — purely visual, styled by the preset ('bottom-to-top'reverses the overlapping stack). The standard exposes it asdata-stack-directionon the group container (the root<span>, only in group mode), so plain CSS can target it.
Single vs Group Mode
The component automatically detects mode from group():
- Group mode: when
group()is a non-empty array,isGroup()istrueand the component iteratesgroup(). The single-modeimageUrl/initials/placeholder branch is not rendered. - Single mode: when
group()isundefinedor[],isGroup()isfalseand the component rendersimageUrl(orinitials, or placeholder) as a single avatar.
AVATAR_STANDARD_COMPONENT_TOKEN
InjectionToken that allows replacing the default AvatarStandardComponent with a custom implementation. Provide a Type<AvatarBaseComponent> to override.
Extending the base class
import {
ChangeDetectionStrategy,
Component,
computed,
input,
signal,
ViewEncapsulation,
} from '@angular/core';
import {
AvatarBaseComponent,
AvatarComponent,
AVATAR_STANDARD_COMPONENT_TOKEN,
IAvatarItem,
SmartAvatarSize,
} from '@smartsoft001/angular';
@Component({
selector: 'docs-custom-avatar',
template: `
<span [class]="containerClasses()">
@if (isGroup()) {
@for (item of group(); track item.id) {
<span class="docs-avatar__group-item">
@if (item.imageUrl; as url) {
<img [src]="url" alt="" />
} @else {
<span class="docs-avatar__initials">{{ item.initials }}</span>
}
</span>
}
} @else if (imageUrl(); as url) {
<img [src]="url" alt="" />
} @else if (initials(); as text) {
<span class="docs-avatar__initials">{{ text }}</span>
} @else {
<span class="docs-avatar__placeholder" aria-hidden="true"
>·</span
>
}
@if (notificationPosition(); as position) {
<span
class="docs-avatar__notification"
[attr.data-position]="position"
></span>
}
</span>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomAvatarComponent extends AvatarBaseComponent {
// NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
override cssClass = input<string>('');
containerClasses = computed(() =>
[
'docs-avatar',
`docs-avatar--${this.size()}`,
`docs-avatar--${this.shape()}`,
this.cssClass(),
]
.filter(Boolean)
.join(' '),
);
}
@Component({
selector: 'docs-avatar-custom-example',
imports: [AvatarComponent],
providers: [
{
provide: AVATAR_STANDARD_COMPONENT_TOKEN,
useValue: CustomAvatarComponent,
},
],
template: `
<smart-avatar
[initials]="initials"
[size]="size()"
shape="rounded"
notificationPosition="top"
/>
<smart-avatar [group]="team" size="sm" />
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvatarCustomExampleComponent {
initials = 'TW';
size = signal<SmartAvatarSize>('md');
team: IAvatarItem[] = [
{ id: 'ana', initials: 'AK' },
{ id: 'bo', initials: 'BS' },
{ id: 'cai', initials: 'CL' },
];
}
Source
The component lives in packages/shared/angular/src/lib/components/avatar and is documented for Claude Code by the angular-components-avatar skill.