Avatar
The Avatar component displays user profile images with intelligent fallbacks. When no image is provided, it shows user initials or a placeholder icon. The component supports various sizes and can be grouped together for team displays.
#Import
use gpui_kit::component::avatar::{Avatar, AvatarGroup};// Installed in ui/components. That directory is on the Slint include path, so import by file name:
import { AvatarSize, Avatar, AvatarGroup } from "avatar.slint";use iced::widget::{column, container, mouse_area, row, scrollable, stack, svg, text};
use iced::{Alignment, Color, Element, Length, Padding, Task, Theme};#Usage
#Image
// The image wins when there is one; the initials behind it are the fallback
// Slint also writes into its first box.
let image = row![shown().initials("JL"), shown()]
.spacing(theme::SPACING_MD)
.align_y(Alignment::Center);You can create an Avatar by providing an image source URL and a user name:
Avatar::new()
.name("John Doe")
.src("https://example.com/avatar.jpg")VerticalLayout {
spacing: 12px;
alignment: start;
Avatar {
source: root.portrait;
initials: "JL";
}
Avatar {
source: root.portrait;
}
}#Fallback
// Neither image nor initials: the fill alone. The live avatar repeats the
// three fallbacks and takes the switches below.
let fallback = column![
row![
Avatar::new().initials("JL"),
Avatar::new(),
Avatar::new().icon(user_icon(32.0 * 0.45)),
]
.spacing(theme::SPACING_MD)
.align_y(Alignment::Center),
clipped(switchable),
row![
switch(
"image",
app.fallback == Fallback::Image,
Message::Fallback(Fallback::Image)
),
switch(
"initials",
app.fallback == Fallback::Initials,
Message::Fallback(Fallback::Initials)
),
switch(
"icon",
app.fallback == Fallback::Icon,
Message::Fallback(Fallback::Icon)
),
]
.spacing(theme::SPACING_SM),
]
.spacing(theme::SPACING_MD);When no image source is provided, the Avatar displays user initials with an automatically generated color background:
// Shows "JD" initials with colored background
Avatar::new()
.name("John Doe")
// Shows "JS" initials
Avatar::new()
.name("Jane Smith")VerticalLayout {
spacing: 12px;
alignment: start;
Avatar {
initials: "JL";
}
Avatar {}
Avatar {
icon: "box";
}
}The color is derived from the initials, so the same person always gets the same one. It comes from a ring of 12 evenly spaced OkLCH hues held at a fixed lightness and chroma, which keeps every avatar at the same visual weight and its text above WCAG AA contrast in both the light and dark themes. The outline follows the same hue; an Avatar showing an image keeps the neutral border.
#Group
// Three overlapping avatars, and the overflow row: five shown behind a chip
// because eleven names is more than the limit Slint sets.
let group = column![
avatar_group(vec![shown(), shown(), shown()], false),
avatar_group((0..5).map(|_| shown()).collect(), true),
]
.spacing(theme::SPACING_LG);For anonymous users or when no name is provided:
use gpui_kit::component::IconName;
// Default user icon placeholder
Avatar::new()
// Custom placeholder icon
Avatar::new()
.placeholder(IconName::Building2)VerticalLayout {
spacing: 20px;
alignment: start;
AvatarGroup {
Avatar {
source: root.portrait;
horizontal-stretch: 0;
}
Avatar {
source: root.portrait;
horizontal-stretch: 0;
}
Avatar {
source: root.portrait;
horizontal-stretch: 0;
}
}
AvatarGroup {
limit: 5;
ellipsis: true;
overflow-count: 11;
avatar-size: AvatarSize.medium;
Avatar {
source: root.portrait;
horizontal-stretch: 0;
}
Avatar {
source: root.portrait;
horizontal-stretch: 0;
}
Avatar {
source: root.portrait;
horizontal-stretch: 0;
}
Avatar {
source: root.portrait;
horizontal-stretch: 0;
}
Avatar {
source: root.portrait;
horizontal-stretch: 0;
}
}
}#Custom shape
Avatar::new()
.src("https://example.com/avatar.jpg")
.with_size(px(100.))
.border_3()
.border_color(cx.theme().foreground)
.shadow_sm()
.rounded(px(20.)) // Custom border radiusAvatar {
source: root.portrait;
custom-edge: 100px;
radius: 20px;
}// A custom edge overrides the size ladder, and the radius is the caller's,
// so this is a rounded rectangle rather than the default circle.
let custom_shape = shown().edge(100.0).radius(20.0);#Custom style
// The avatar automatically generates colors based on the name
// Different names will get different colors from the color palette
Avatar::new().name("Alice") // Gets one color
Avatar::new().name("Bob") // Gets a different color
Avatar::new().name("Charlie") // Gets another colorAvatar {
source: root.portrait;
custom-edge: 100px;
border-width: 3px;
border-color: Theme.foreground;
shadow-blur: 4px;
shadow-offset-y: 2px;
shadow-color: Theme.shadow;
}// The 3px edge and the drop shadow are the chrome Slint draws; the fill and
// the initials colour are the two colours a caller can override.
let custom_style = row![
labelled("edge 100, chrome", chrome(shown().edge(100.0).radius(20.0))),
labelled(
"custom fill",
Avatar::new()
.initials("JL")
.fill(Color::from_rgb(0.13, 0.13, 0.13))
.initials_color(Color::WHITE),
),
]
.spacing(theme::SPACING_XL)
.align_y(Alignment::Start);#Avatar Sizes
Avatar::new()
.name("John Doe")
.xsmall()
Avatar::new()
.name("John Doe")
.small()
Avatar::new()
.name("John Doe") // 48px (default medium)
Avatar::new()
.name("John Doe")
.large()
// Custom size
Avatar::new()
.name("John Doe")
.with_size(px(100.))Avatar {
initials: "XS";
size: AvatarSize.xsmall;
}
Avatar {
initials: "SM";
size: AvatarSize.small;
}
Avatar {
initials: "MD";
size: AvatarSize.medium;
}
Avatar {
initials: "LG";
size: AvatarSize.large;
}// The Slint ladder is 20 / 24 / 32 / 40 / 56.
let sizes = row![
labelled(
"xs 20",
Avatar::new().initials("XS").size(AvatarSize::XSmall)
),
labelled(
"sm 24",
Avatar::new().initials("SM").size(AvatarSize::Small)
),
labelled(
"md 32",
Avatar::new().initials("MD").size(AvatarSize::Medium)
),
labelled(
"lg 40",
Avatar::new().initials("LG").size(AvatarSize::Large)
),
labelled(
"xl 56",
Avatar::new().initials("XL").size(AvatarSize::XLarge)
),
]
.spacing(theme::SPACING_MD)
.align_y(Alignment::Start);#Team Display
use gpui_kit::component::{h_flex, v_flex};
v_flex()
.gap_4()
.child("Development Team")
.child(
AvatarGroup::new()
.limit(4)
.ellipsis()
.child(Avatar::new().name("Alice Johnson").src("https://example.com/alice.jpg"))
.child(Avatar::new().name("Bob Smith").src("https://example.com/bob.jpg"))
.child(Avatar::new().name("Charlie Brown"))
.child(Avatar::new().name("Diana Prince"))
.child(Avatar::new().name("Eve Wilson"))
)#User Profile Header
h_flex()
.items_center()
.gap_4()
.child(
Avatar::new()
.src("https://example.com/profile.jpg")
.name("John Doe")
.large()
.border_2()
.border_color(cx.theme().primary)
)
.child(
v_flex()
.child("John Doe")
.child("Software Engineer")
)#Anonymous User
use gpui_kit::component::IconName;
Avatar::new()
.placeholder(IconName::UserCircle)
.medium()#AvatarGroup
The AvatarGroup component allows you to display multiple avatars in a compact, overlapping layout:
#Basic Group
AvatarGroup::new()
.child(Avatar::new().src("https://example.com/user1.jpg"))
.child(Avatar::new().src("https://example.com/user2.jpg"))
.child(Avatar::new().src("https://example.com/user3.jpg"))
.child(Avatar::new().name("John Doe"))#Group with Limit
AvatarGroup::new()
.limit(3) // Show maximum 3 avatars
.child(Avatar::new().src("https://example.com/user1.jpg"))
.child(Avatar::new().src("https://example.com/user2.jpg"))
.child(Avatar::new().src("https://example.com/user3.jpg"))
.child(Avatar::new().src("https://example.com/user4.jpg")) // Hidden
.child(Avatar::new().src("https://example.com/user5.jpg")) // Hidden#Group with Ellipsis
Show an ellipsis indicator when avatars are hidden due to the limit.
In this example, only 3 avatars are shown, and ”…” indicates there are more:
AvatarGroup::new()
.limit(3)
.ellipsis() // Shows "..." when limit is exceeded
.child(Avatar::new().src("https://example.com/user1.jpg"))
.child(Avatar::new().src("https://example.com/user2.jpg"))
.child(Avatar::new().src("https://example.com/user3.jpg"))
.child(Avatar::new().src("https://example.com/user4.jpg"))
.child(Avatar::new().src("https://example.com/user5.jpg"))#Group Sizes
The [Sizeable] trait can also be applied to AvatarGroup, and it will set the size for all contained avatars.
// Extra small group
AvatarGroup::new()
.xsmall()
.child(Avatar::new().name("A"))
.child(Avatar::new().name("B"))
.child(Avatar::new().name("C"))
// Small group
AvatarGroup::new()
.small()
.child(Avatar::new().name("A"))
.child(Avatar::new().name("B"))
// Medium group (default)
AvatarGroup::new()
.child(Avatar::new().name("A"))
.child(Avatar::new().name("B"))
// Large group
AvatarGroup::new()
.large()
.child(Avatar::new().name("A"))
.child(Avatar::new().name("B"))#Adding Multiple Avatars
let avatars = vec![
Avatar::new().src("https://example.com/user1.jpg"),
Avatar::new().src("https://example.com/user2.jpg"),
Avatar::new().name("John Doe"),
];
AvatarGroup::new()
.children(avatars)
.limit(5)
.ellipsis()#API Reference
#AvatarSize
xsmall, small, medium, large, xlarge
#Avatar
A user portrait. Mirrors GPUI Component's `Avatar`: an image, initials or icon fallback, sizes, grouped overlap, and optional custom radius or chrome.
| Name | Kind | Type | Default | Description |
|---|---|---|---|---|
source | in | image | ||
initials | in | string | ||
size | in | AvatarSize | AvatarSize.medium | |
fill | in | color | Theme.secondary | |
initials-color | in | color | Theme.secondary-foreground | The initials must contrast with `fill`; a caller that darkens the fill sets this too. |
icon | in | string | "user" | |
custom-edge | in | length | Overrides `size` when greater than zero. | |
radius | in | length | Zero uses a circle. Set an explicit length for rounded-rectangle avatars. | |
border-width | in | length | 1px | |
border-color | in | color | Theme.border | |
shadow-blur | in | length | ||
shadow-offset-y | in | length | 1px | |
shadow-color | in | color | Theme.shadow |
#AvatarGroup
Overlapping avatars. GPUI defaults to showing three; set `limit` and `ellipsis` for overflow rows like the story gallery.
| Name | Kind | Type | Default |
|---|---|---|---|
limit | in | int | 3 |
ellipsis | in | bool | |
overflow-count | in | int | |
avatar-size | in | AvatarSize | AvatarSize.medium |