Files
Htmx/docs/Components/Avatar.md
T
2026-05-05 23:55:26 +05:00

4.6 KiB
Raw Blame History

Avatar

A circular user icon. Shows a profile photo when a URL is provided, or falls back to text (typically initials) on a neutral background.


Quick example

// Initials only
new Avatar(fallback: "JD")

// With a profile photo
new Avatar(fallback: "Jane Doe", src: "/avatars/jane.jpg")

All the options

public Avatar(
    string fallback,
    string? src  = null,
    string size  = "default")
Parameter What it does
fallback The text shown when no image is available — typically initials like "JD". Also used as the alt attribute on the image for screen readers.
src URL of the profile photo. If omitted, the fallback is shown.
size How big the circle is: "sm" (32px), "default" (40px), "lg" (56px), "xl" (80px)

Real-world examples

Initials in different sizes

new Avatar(fallback: "SM", size: "sm")      // 32×32 — good for compact lists
new Avatar(fallback: "JD", size: "default") // 40×40 — standard nav bar
new Avatar(fallback: "LG", size: "lg")      // 56×56 — profile card header
new Avatar(fallback: "XL", size: "xl")      // 80×80 — full profile page

Profile page header

new Avatar(
    fallback: user.DisplayName ?? "?",
    src:      user.AvatarUrl,
    size:     "xl")

The fallback is always required — even with a src — because it becomes the alt text on the <img> tag.

Overlapping avatar stack (e.g. "3 team members")

Wrap multiple avatars in a flex container with negative spacing:

<div class="flex -space-x-2">
  $$Avatar1$$
  $$Avatar2$$
  $$Avatar3$$
</div>

How it works

The avatar is a <span> clipped to a circle with rounded-full overflow-hidden. When a src is given, an <img> fills the circle using object-cover so the photo does not stretch. When there is no src, a <span> with a muted background shows the fallback text centred inside the circle.

The component does not handle broken image URLs. If you need a fallback when an image 404s, add an onerror attribute in the surrounding HTML.

The Avatar does not extract initials from full names — do that yourself before constructing it. "Jane Doe""JD" is two lines of C# and is better kept in your own code.


Complete page example

Templates/ProfilePage.htmx

<div class="max-w-lg mx-auto py-10">
  <div class="flex items-center gap-4 mb-6">
    $$UserAvatar$$
    <div>
      <h1 class="text-xl font-bold">$$DisplayName$$</h1>
      <p class="text-sm text-muted-foreground">$$Email$$</p>
    </div>
  </div>
  <p class="text-sm">Member since $$JoinDate$$</p>
</div>

Templates/ProfilePage.htmx.cs

namespace Htmx.ApiDemo.Templates;

public sealed class ProfilePage : ProfilePageBase
{
    private readonly IHtmxComponent _avatar;
    private readonly byte[] _displayName;
    private readonly byte[] _email;
    private readonly byte[] _joinDate;

    public ProfilePage(AppUser user)
    {
        _avatar = new Components.Avatar(
            fallback: GetInitials(user.DisplayName),
            size: "lg");

        _displayName = (user.DisplayName ?? "Unknown").ToUtf8Bytes();
        _email       = user.Email.ToUtf8Bytes();
        _joinDate    = user.CreatedAt.ToString("MMMM yyyy").ToUtf8Bytes();
    }

    private static string GetInitials(string? name)
    {
        if (string.IsNullOrWhiteSpace(name)) return "?";
        var parts = name.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        return parts.Length >= 2
            ? $"{parts[0][0]}{parts[^1][0]}"
            : name[..1].ToUpperInvariant();
    }

    protected override void RenderUserAvatar(HtmxRenderContext ctx)
        => _avatar.Render(ctx.Next());
    protected override void RenderDisplayName(HtmxRenderContext ctx)
        => ctx.Writer.WriteUtf8(_displayName);
    protected override void RenderEmail(HtmxRenderContext ctx)
        => ctx.Writer.WriteUtf8(_email);
    protected override void RenderJoinDate(HtmxRenderContext ctx)
        => ctx.Writer.WriteUtf8(_joinDate);
}

GET handler

[Handler]
[MapGet("/profile")]
public static partial class GetProfileHandler
{
    public record Query();

    private static async Task<IResult> HandleAsync(
        Query _,
        HttpContext ctx,
        MongoDbService db,
        CancellationToken ct)
    {
        var email = ctx.User.FindFirst(ClaimTypes.Email)?.Value ?? "";
        var user  = await db.FindByNormalizedEmailAsync(email.ToUpperInvariant(), ct);
        if (user is null) return Results.Redirect("/login");
        return await ctx.WriteHtmxPage(new ProfilePage(user), title: "Profile");
    }
}