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

161 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `<img>` tag.
### Overlapping avatar stack (e.g. "3 team members")
Wrap multiple avatars in a flex container with negative spacing:
```html
<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`**
```html
<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`**
```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<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");
}
}
```