Svelte 5 component · fractalsvelte-ui · Display
Avatar
A visual representation of an entity, user profile, or workspace. Displays an image with automatic fallback to initials or a generic glyph, alongside an optional presence status indicator.
Install
Avatar is a Svelte 5 avatar component in @fractaldesign/fractalsvelte-ui. It works in SvelteKit and in any Svelte 5 app.
pnpm add @fractaldesign/fractalsvelte-uiImport
import { Avatar } from '@fractaldesign/fractalsvelte-ui';Usage
<Avatar alt={'Avatar'} />Props
Also accepts every attribute of HTMLAttributes, spread onto
the root element.
| Name | Type | Default |
|---|---|---|
src | string | — |
alt | string | 'Avatar' |
initials | string | — |
status | AvatarStatus 'online' | 'busy' | 'away' | 'offline' | — |
size | Size 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' |
shape | Shape 'square' | 'rounded' | 'pill' | 'pill' |
class | string | '' |
Snippets
| Name | Type | Default |
|---|---|---|
fallback | Snippet | — |
Source
Show the Avatar.svelte source
<script lang="ts">
import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
import type { Size, Shape } from '../../types';
export type AvatarStatus = 'online' | 'busy' | 'away' | 'offline';
interface Props extends HTMLAttributes<HTMLDivElement> {
src?: string;
alt?: string;
initials?: string;
status?: AvatarStatus;
size?: Size;
shape?: Shape;
class?: string;
fallback?: Snippet;
}
let {
src,
alt = 'Avatar',
initials,
status,
size = 'md',
shape = 'pill',
class: className = '',
fallback,
...restProps
}: Props = $props();
let imgError = $state(false);
function handleImgError() {
imgError = true;
}
</script>
<div
class="f-avatar {className}"
data-size={size}
data-shape={shape}
{...restProps}
>
{#if src && !imgError}
<img
{src}
{alt}
class="f-avatar-img"
onerror={handleImgError}
/>
{:else if initials}
<span class="f-avatar-initials">{initials}</span>
{:else if fallback}
<span class="f-avatar-fallback">
{@render fallback()}
</span>
{:else}
<!-- Default user icon silhouette -->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="f-avatar-icon"
aria-hidden="true"
>
<path
d="M12 12c2.7 0 4.8-2.1 4.8-4.8S14.7 2.4 12 2.4 7.2 4.5 7.2 7.2 9.3 12 12 12zm0 2.4c-3.2 0-9.6 1.6-9.6 4.8v2.4h19.2v-2.4c0-3.2-6.4-4.8-9.6-4.8z"
/>
</svg>
{/if}
{#if status}
<span
class="f-avatar-status"
data-status={status}
aria-label={`Status: ${status}`}
></span>
{/if}
</div>
<style>
@layer fractalsvelte-ui.components {
.f-avatar {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
vertical-align: middle;
background: var(--f-sunken);
color: var(--f-text-muted);
border: 1px solid var(--f-border);
box-sizing: border-box;
overflow: visible;
flex-shrink: 0;
user-select: none;
}
.f-avatar-img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: inherit;
display: block;
}
.f-avatar-initials {
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--f-text);
line-height: 1;
}
.f-avatar-fallback,
.f-avatar-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 60%;
height: 60%;
color: var(--f-text-subtle);
}
/* Shapes */
.f-avatar[data-shape='pill'] {
border-radius: var(--f-radius-full);
}
.f-avatar[data-shape='rounded'] {
border-radius: var(--f-radius-md);
}
.f-avatar[data-shape='square'] {
border-radius: 0;
}
/* Sizes */
.f-avatar[data-size='xs'] {
width: 24px;
height: 24px;
font-size: 10px;
}
.f-avatar[data-size='sm'] {
width: 32px;
height: 32px;
font-size: var(--f-text-xs);
}
.f-avatar[data-size='md'] {
width: 40px;
height: 40px;
font-size: var(--f-text-sm);
}
.f-avatar[data-size='lg'] {
width: 48px;
height: 48px;
font-size: var(--f-text-base);
}
.f-avatar[data-size='xl'] {
width: 60px;
height: 60px;
font-size: var(--f-text-xl);
}
/* Status Dot */
.f-avatar-status {
position: absolute;
bottom: -1px;
right: -1px;
border-radius: var(--f-radius-full);
box-sizing: border-box;
border: 2px solid var(--f-surface);
z-index: 1;
}
.f-avatar[data-size='xs'] .f-avatar-status {
width: 8px;
height: 8px;
border-width: 1.5px;
}
.f-avatar[data-size='sm'] .f-avatar-status {
width: 10px;
height: 10px;
border-width: 2px;
}
.f-avatar[data-size='md'] .f-avatar-status {
width: 12px;
height: 12px;
border-width: 2px;
}
.f-avatar[data-size='lg'] .f-avatar-status {
width: 14px;
height: 14px;
border-width: 2.5px;
}
.f-avatar[data-size='xl'] .f-avatar-status {
width: 16px;
height: 16px;
border-width: 3px;
}
.f-avatar-status[data-status='online'] {
background-color: var(--f-success);
}
.f-avatar-status[data-status='busy'] {
background-color: var(--f-danger);
}
.f-avatar-status[data-status='away'] {
background-color: var(--f-warning);
}
.f-avatar-status[data-status='offline'] {
background-color: var(--f-text-subtle);
}
}
</style>Related components
AvatarGroup A stacked container that clusters related avatars with consistent overlapping margins and elevation borders.Badge A compact status indicator pill used to highlight counts, states, or tags.Banner A full-width announcement strip intended for global notices, system maintenance alerts, and calls to action.Counter An animated rolling number odometer counter with vertical digit reels that slide smoothly into position upon…DotMatrix A retro LED dot-matrix display rendering alphanumeric text on a 5x7 phosphor grid with authentic glow, pitch…EmptyState A placeholder container displayed when lists, tables, or views have no data to render.