Avatar

iOS 16.4+iPadOS 16.4+

Displays a user's profile picture, with initials as a fallback.

FLXAvatar example
import SwiftUIstruct AvatarUsageExample: View {    var body: some View {        HStack(spacing: FLXSpacing.md) {            FLXAvatar(size: .small) {                FLXAvatarFallback("FM")                FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))            }            FLXAvatar(size: .medium) {                FLXAvatarFallback("FM")                FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))            }            FLXAvatar(size: .large) {                FLXAvatarFallback("FM")                FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))            }            FLXAvatar(size: .medium) {                FLXAvatarFallback("FM")                FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))                FLXAvatarBadge()            }        }        .padding(FLXSpacing.xl)    }}

Installation

Requires the design system foundation (tokens and theme).

pnpm dlx shadcn@latest add https://swiftui.flexnative.com/r/ios-avatar.json

Usage

FLXAvatar(size: .medium) {    FLXAvatarFallback("FM")    FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))}

Composition

FLXAvatar stacks its content in a ZStack. Use the following composition to build an avatar:

FLXAvatar├── FLXAvatarFallback├── FLXAvatarImage└── FLXAvatarBadge

Put FLXAvatarFallback first so it sits behind FLXAvatarImage, which stays transparent until the image finishes loading. FLXAvatarFallback uppercases its text. FLXAvatarBadge is optional and always pins itself to the bottom-trailing corner.

Sizes

Three sizes, set via size:. Avatars grow with Dynamic Type up to .accessibility2 (about twice their size) and stop there: they're pictures, not text to read, so they don't need to fill the screen at the largest text sizes.

FLXAvatar sizes
FLXAvatar(size: .small) {  FLXAvatarFallback("FM")  FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))}FLXAvatar(size: .medium) {  FLXAvatarFallback("FM")  FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))}FLXAvatar(size: .large) {  FLXAvatarFallback("FM")  FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))}

Badge

Overlay a status indicator by adding FLXAvatarBadge alongside the other content. The badge doesn't inherit the avatar's size, so pass a matching size: for .small and .large avatars:

FLXAvatar with badge
FLXAvatar(size: .medium) {  FLXAvatarFallback("FM")  FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))  FLXAvatarBadge()}

Groups

Wrap a stack of avatars in FLXAvatarGroup to overlap them, and finish the stack with FLXAvatarGroupCount for an overflow indicator:

FLXAvatarGroup with overflow count
FLXAvatarGroup {  FLXAvatar(size: .medium) {      FLXAvatarFallback("FM")      FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))  }  FLXAvatar(size: .medium) {      FLXAvatarFallback("FM")      FLXAvatarImage(url: URL(string: "https://github.com/felipemenezes098.png"))  }  FLXAvatar(size: .medium) {      FLXAvatarFallback("PD")      FLXAvatarImage(url: URL(string: "https://github.com/peduarte.png"))  }  FLXAvatarGroupCount(count: 5, size: .medium)}

Custom styling

FLXAvatarStyle is an FLX protocol. Override it for a subtree via .flxAvatarStyle(_:):

FLXAvatar custom styling
struct FLXRingAvatarStyle: FLXAvatarStyle {  func makeBody(configuration: Configuration) -> some View {      FLXRingAvatarStyleBody(configuration: configuration)  }}struct FLXRingAvatarStyleBody: View {  @ScaledMetric(relativeTo: .body) private var scale: CGFloat = 1  let configuration: FLXAvatarStyleConfiguration  var body: some View {      ZStack {          configuration.content      }      .flxFont(configuration.size.typography)      .foregroundStyle(.orange)      .frame(width: configuration.size.diameter * scale, height: configuration.size.diameter * scale)      .padding(FLXSpacing.xs)      .overlay(          Circle().strokeBorder(              AngularGradient(colors: [.yellow, .orange, .pink, .yellow], center: .center),              lineWidth: FLXBorderWidth.thick          )      )  }}HStack(spacing: FLXSpacing.xl) {  FLXAvatar(size: .large) {      FLXAvatarFallback("FM")      FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))  }  FLXAvatar(size: .large) {      FLXAvatarFallback("FM")      FLXAvatarImage(url: URL(string: "https://github.com/shadcn.png"))  }  .flxAvatarStyle(FLXRingAvatarStyle())}

API Reference

FLXAvatar

init(    size: FLXAvatarSize = .medium,    @ViewBuilder content: @escaping () -> Content)

FLXAvatarImage

init(url: URL?)

FLXAvatarFallback

init(_ text: String)

FLXAvatarBadge

init(size: FLXAvatarSize = .medium)

FLXAvatarGroup

init(@ViewBuilder content: @escaping () -> Content)

FLXAvatarGroupCount

init(count: Int, size: FLXAvatarSize = .medium)

Sizes

CaseDescription
smallCompact diameter, for dense lists.
mediumDefault diameter.
largeLargest diameter, for profile headers.