Skip to content

AvatarGroup

Avatars stacked or in a grid, the rest counted.

Solidtype_heroui.Avatar_group is a <div> of Solidtype_heroui.Avatars, with the classes HeroUI’s own avatarGroupVariants gives. It gives the avatars inside it its size, color and variant, each where an avatar sets none itself, shows the first max of them, and counts the rest. Kobalte has no group.

JDKWECMB
avatar_group_usage.mlx
module Jsx = Solidtype.Jsx
module For = Solidtype.For
module Avatar = Solidtype_heroui.Avatar
module Avatar_group = Solidtype_heroui.Avatar_group
let users =
[|
( "John Doe",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
);
( "Kate Wilson",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg"
);
( "Emily Chen",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"
);
( "Michael Brown",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg"
);
( "Olivia Davis",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg" );
|]
(* "John Doe" is "JD". *)
let initials name =
String.split_on_char ' ' name
|> List.filter_map (fun word ->
if String.length word > 0 then Some (String.make 1 word.[0]) else None)
|> String.concat ""
let make () =
<Avatar_group>
<For each=(fun () -> Array.sub users 0 4)>
(fun (name, image) _ ->
<Avatar>
<Avatar.Image alt=(fun () -> name) src=(fun () -> image) />
<Avatar.Fallback>
(fun () -> Jsx.string (initials name))
</Avatar.Fallback>
</Avatar>)
</For>
</Avatar_group>
<Avatar_group>
<Avatar>
<Avatar.Image alt="Ada Lovelace" src="/ada.png" />
<Avatar.Fallback>"AL"</Avatar.Fallback>
</Avatar>
<Avatar_group.Count>"+3"</Avatar_group.Count>
</Avatar_group>

max shows the first avatars and counts the rest.

JDKWEC+2
avatar_group_max.mlx
module Jsx = Solidtype.Jsx
module For = Solidtype.For
module Avatar = Solidtype_heroui.Avatar
module Avatar_group = Solidtype_heroui.Avatar_group
let users =
[|
( "John Doe",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
);
( "Kate Wilson",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg"
);
( "Emily Chen",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"
);
( "Michael Brown",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg"
);
( "Olivia Davis",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg" );
|]
(* "John Doe" is "JD". *)
let initials name =
String.split_on_char ' ' name
|> List.filter_map (fun word ->
if String.length word > 0 then Some (String.make 1 word.[0]) else None)
|> String.concat ""
let make () =
<Avatar_group max=3>
<For each=(fun () -> users)>
(fun (name, image) _ ->
<Avatar>
<Avatar.Image alt=(fun () -> name) src=(fun () -> image) />
<Avatar.Fallback>
(fun () -> Jsx.string (initials name))
</Avatar.Fallback>
</Avatar>)
</For>
</Avatar_group>

For a total the page knows, an Avatar_group.Count of its own: the group counts none itself, and shows it whatever its max.

JDKWEC+9
avatar_group_count.mlx
module Jsx = Solidtype.Jsx
module For = Solidtype.For
module Avatar = Solidtype_heroui.Avatar
module Avatar_group = Solidtype_heroui.Avatar_group
let users =
[|
( "John Doe",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
);
( "Kate Wilson",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg"
);
( "Emily Chen",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"
);
( "Michael Brown",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg"
);
( "Olivia Davis",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg" );
|]
(* "John Doe" is "JD". *)
let initials name =
String.split_on_char ' ' name
|> List.filter_map (fun word ->
if String.length word > 0 then Some (String.make 1 word.[0]) else None)
|> String.concat ""
let make () =
let total = 12 in
<Avatar_group size=`sm>
<For each=(fun () -> Array.sub users 0 3)>
(fun (name, image) _ ->
<Avatar>
<Avatar.Image alt=(fun () -> name) src=(fun () -> image) />
<Avatar.Fallback>
(fun () -> Jsx.string (initials name))
</Avatar.Fallback>
</Avatar>)
</For>
<Avatar_group.Count>
(fun () -> Jsx.string ("+" ^ string_of_int (total - 3)))
</Avatar_group.Count>
</Avatar_group>

Small

JDKWECMB

Medium (default)

JDKWECMB

Large

JDKWECMB
avatar_group_sizes.mlx
module Jsx = Solidtype.Jsx
module For = Solidtype.For
module Avatar = Solidtype_heroui.Avatar
module Avatar_group = Solidtype_heroui.Avatar_group
let users =
[|
( "John Doe",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
);
( "Kate Wilson",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg"
);
( "Emily Chen",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"
);
( "Michael Brown",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg"
);
( "Olivia Davis",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg" );
|]
(* "John Doe" is "JD". *)
let initials name =
String.split_on_char ' ' name
|> List.filter_map (fun word ->
if String.length word > 0 then Some (String.make 1 word.[0]) else None)
|> String.concat ""
let make () =
<div class_="flex flex-col items-center gap-6">
<div class_="flex flex-col items-center gap-2">
<p class_="text-sm text-muted">"Small"</p>
<Avatar_group size=`sm>
<For each=(fun () -> Array.sub users 0 4)>
(fun (name, image) _ ->
<Avatar>
<Avatar.Image alt=(fun () -> name) src=(fun () -> image) />
<Avatar.Fallback>
(fun () -> Jsx.string (initials name))
</Avatar.Fallback>
</Avatar>)
</For>
</Avatar_group>
</div>
<div class_="flex flex-col items-center gap-2">
<p class_="text-sm text-muted">"Medium (default)"</p>
<Avatar_group size=`md>
<For each=(fun () -> Array.sub users 0 4)>
(fun (name, image) _ ->
<Avatar>
<Avatar.Image alt=(fun () -> name) src=(fun () -> image) />
<Avatar.Fallback>
(fun () -> Jsx.string (initials name))
</Avatar.Fallback>
</Avatar>)
</For>
</Avatar_group>
</div>
<div class_="flex flex-col items-center gap-2">
<p class_="text-sm text-muted">"Large"</p>
<Avatar_group size=`lg>
<For each=(fun () -> Array.sub users 0 4)>
(fun (name, image) _ ->
<Avatar>
<Avatar.Image alt=(fun () -> name) src=(fun () -> image) />
<Avatar.Fallback>
(fun () -> Jsx.string (initials name))
</Avatar.Fallback>
</Avatar>)
</For>
</Avatar_group>
</div>
</div>
JDKWECMBOD
avatar_group_grid.mlx
module Jsx = Solidtype.Jsx
module For = Solidtype.For
module Avatar = Solidtype_heroui.Avatar
module Avatar_group = Solidtype_heroui.Avatar_group
let users =
[|
( "John Doe",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
);
( "Kate Wilson",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg"
);
( "Emily Chen",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"
);
( "Michael Brown",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg"
);
( "Olivia Davis",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg" );
|]
(* "John Doe" is "JD". *)
let initials name =
String.split_on_char ' ' name
|> List.filter_map (fun word ->
if String.length word > 0 then Some (String.make 1 word.[0]) else None)
|> String.concat ""
let make () =
<Avatar_group is_grid=true max=5>
<For each=(fun () -> users)>
(fun (name, image) _ ->
<Avatar>
<Avatar.Image alt=(fun () -> name) src=(fun () -> image) />
<Avatar.Fallback>
(fun () -> Jsx.string (initials name))
</Avatar.Fallback>
</Avatar>)
</For>
</Avatar_group>

`clip, the default, leaves a crescent seam between avatars, and `ring a solid outline; neither applies to a grid.

clip

JDABECSM+2

ring

JDABECSM+2
avatar_group_overlap.mlx
module Avatar = Solidtype_heroui.Avatar
module Avatar_group = Solidtype_heroui.Avatar_group
let group overlap =
<Avatar_group overlap=(fun () -> overlap) size=`lg>
<Avatar>
<Avatar.Image
alt="John"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
/>
<Avatar.Fallback>"JD"</Avatar.Fallback>
</Avatar>
<Avatar><Avatar.Fallback>"AB"</Avatar.Fallback></Avatar>
<Avatar>
<Avatar.Image
alt="Emily"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"
/>
<Avatar.Fallback>"EC"</Avatar.Fallback>
</Avatar>
<Avatar><Avatar.Fallback>"SM"</Avatar.Fallback></Avatar>
<Avatar_group.Count>"+2"</Avatar_group.Count>
</Avatar_group>
(* A striped backdrop, which shows the seam each overlap leaves. *)
let backdrop child =
<div class_="relative inline-flex rounded-xl p-6">
<div
aria_hidden=true
class_="pointer-events-none absolute inset-0 rounded-xl \
bg-[repeating-linear-gradient(-45deg,transparent_0_10px,color-mix(in_oklab,var(--danger)_20%,transparent)_10px_11px)]"
/>
<div class_="relative z-10">(fun () -> Solidtype.Jsx.element child)</div>
</div>
let make () =
<div class_="flex flex-col items-start gap-6">
<div class_="flex flex-col gap-2">
<p class_="text-sm text-muted">"clip"</p>
(fun () -> Solidtype.Jsx.element (backdrop (group `clip)))
</div>
<div class_="flex flex-col gap-2">
<p class_="text-sm text-muted">"ring"</p>
(fun () -> Solidtype.Jsx.element (backdrop (group `ring)))
</div>
</div>

React counts a group’s own children. Solid cannot single them out, so an avatar joins its group as it is made, and is counted in the order the avatars were made: an avatar a For adds later is counted after the others, wherever it is shown.

class_ is added after HeroUI’s classes; a group that means something, as assignees do, takes a role and a name.

JDKWEC
3 assignees
avatar_group_tailwind.mlx
module Jsx = Solidtype.Jsx
module For = Solidtype.For
module Avatar = Solidtype_heroui.Avatar
module Avatar_group = Solidtype_heroui.Avatar_group
let users =
[|
( "John Doe",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
);
( "Kate Wilson",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg"
);
( "Emily Chen",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"
);
( "Michael Brown",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg"
);
( "Olivia Davis",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg" );
|]
(* "John Doe" is "JD". *)
let initials name =
String.split_on_char ' ' name
|> List.filter_map (fun word ->
if String.length word > 0 then Some (String.make 1 word.[0]) else None)
|> String.concat ""
let make () =
<div
class_="inline-flex items-center gap-2.5 rounded-full border \
border-border/70 bg-surface/95 py-1 pr-3 pl-1 shadow-sm ring-1 \
ring-black/[0.04] dark:border-border/80 dark:bg-surface/90 \
dark:ring-white/10">
<Avatar_group
aria_label="Assignees"
class_="[--avatar-group-overlap:0.7rem] [--avatar-group-seam:2px]"
overlap=`clip
role=`group
size=`sm>
<For each=(fun () -> Array.sub users 0 3)>
(fun (name, image) _ ->
<Avatar>
<Avatar.Image alt=(fun () -> name) src=(fun () -> image) />
<Avatar.Fallback>
(fun () -> Jsx.string (initials name))
</Avatar.Fallback>
</Avatar>)
</For>
</Avatar_group>
<span class_="text-xs font-medium text-muted">"3 assignees"</span>
</div>

HeroUI’s classes are global, so a stylesheet restyles every group, as with HeroUI in React. The overlap is --avatar-group-overlap, 0.5rem by default, and the seam --avatar-group-seam.

  • .avatar-group, and .avatar-group--grid, .avatar-group--clip, .avatar-group--ring
  • .avatar-group__count

Each prop beside the name HeroUI’s React component gives it.

React solidtype Type Default Description
size size [ `sm | `md | `lg ] `md The size of each avatar that sets none
color color [ `default | `accent | `success | `warning | `danger ] The color of each avatar that sets none
variant variant [ `default | `soft ] The variant of each avatar that sets none
max max int How many avatars it shows
isGrid is_grid bool false Whether it is a grid, with no overlap
overlap overlap [ `clip | `ring ] `clip How stacked avatars overlap
role role Solidtype.Html.Div.role Its role, `group where it means something
aria-label aria_label string Its name
aria-labelledby aria_labelledby string The id of what names it
className class_ string Added after HeroUI’s classes
children children elements Its avatars, and its count
React solidtype Type Description
size size [ `sm | `md | `lg ] Its size, else the group’s
color color [ `default | `accent | `success | `warning | `danger ] Its color, else the group’s
variant variant [ `default | `soft ] Its variant, else the group’s
className class_ string Added after HeroUI’s classes
children children elements The count, “+3”

Original HeroUI documentation: AvatarGroup.

The same HeroUI AvatarGroup twice, under the same props: in solidtype on the left, in React on the right. Below them, every attribute of the two groups: a marked row is one they disagree on.

solidtype — OCaml, on Solid and Kobalte
ALGHKJBL
React — TypeScript, on React Aria
attributesolidtypeReact