# 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
```csharp
// Initials only
new Avatar(fallback: "JD")
// With a profile photo
new Avatar(fallback: "Jane Doe", src: "/avatars/jane.jpg")
```
---
## All the options
```csharp
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
```csharp
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
```csharp
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 `
` tag.
### Overlapping avatar stack (e.g. "3 team members")
Wrap multiple avatars in a flex container with negative spacing:
```html
$$Avatar1$$
$$Avatar2$$
$$Avatar3$$
```
---
## How it works
The avatar is a `` clipped to a circle with `rounded-full overflow-hidden`. When a `src` is given, an `
` fills the circle using `object-cover` so the photo does not stretch. When there is no `src`, a `` 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`**
```html
$$UserAvatar$$
$$DisplayName$$
$$Email$$
Member since $$JoinDate$$
```
**`Templates/ProfilePage.htmx.cs`**
```csharp
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**
```csharp
[Handler]
[MapGet("/profile")]
public static partial class GetProfileHandler
{
public record Query();
private static async Task 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");
}
}
```