diff --git a/src/Components/DataDisplay/SUIAvatar.razor b/src/Components/DataDisplay/SUIAvatar.razor
index f4a194c..58a3255 100644
--- a/src/Components/DataDisplay/SUIAvatar.razor
+++ b/src/Components/DataDisplay/SUIAvatar.razor
@@ -1,8 +1,13 @@
@namespace Sufficit.Blazor.UI.Components
-@if (!string.IsNullOrWhiteSpace(Src))
+@if (!_imageLoadFailed && !string.IsNullOrWhiteSpace(Src))
{
-
+
}
else
{
@@ -55,6 +60,20 @@ else
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary AdditionalAttributes { get; set; } = new();
+ private string? _renderedSrc;
+ private bool _imageLoadFailed;
+
+ protected override void OnParametersSet()
+ {
+ if (!string.Equals(_renderedSrc, Src, StringComparison.Ordinal))
+ {
+ _renderedSrc = Src;
+ _imageLoadFailed = false;
+ }
+ }
+
+ private void HandleImageError() => _imageLoadFailed = true;
+
///
/// Size suffix. The stylesheet already used --sm/--md/--lg for the avatars
/// rendered inside SUINavGroup and SUINavLink, so this maps onto the
diff --git a/tests/Sufficit.Blazor.UI.Tests/ComponentLifecycleTests.cs b/tests/Sufficit.Blazor.UI.Tests/ComponentLifecycleTests.cs
index 84b3814..4baae64 100644
--- a/tests/Sufficit.Blazor.UI.Tests/ComponentLifecycleTests.cs
+++ b/tests/Sufficit.Blazor.UI.Tests/ComponentLifecycleTests.cs
@@ -58,6 +58,40 @@ public void CardHeader_SupportsLegacyNamedSlotsDuringSuiMigration()
Assert.NotNull(cut.Find("[data-slot='actions']"));
}
+ [Fact]
+ public void Avatar_FallsBackToChildContentWhenImageFails()
+ {
+ using var context = new BunitContext();
+ var cut = context.Render(parameters => parameters
+ .Add(component => component.Src, "https://avatars.tests.local/owner.jpg")
+ .Add(component => component.Alt, string.Empty)
+ .AddChildContent("OP"));
+
+ cut.Find("img").TriggerEvent("onerror", new EventArgs());
+
+ var fallback = cut.Find("div.sui-avatar");
+ Assert.Equal("OP", fallback.TextContent);
+ Assert.Equal("true", fallback.GetAttribute("aria-hidden"));
+ }
+
+ [Fact]
+ public void Avatar_RetriesWhenItsSourceChanges()
+ {
+ using var context = new BunitContext();
+ var cut = context.Render(parameters => parameters
+ .Add(component => component.Src, "https://avatars.tests.local/first.jpg")
+ .AddChildContent("OP"));
+ cut.Find("img").TriggerEvent("onerror", new EventArgs());
+
+ cut.Render(parameters => parameters
+ .Add(component => component.Src, "https://avatars.tests.local/second.jpg")
+ .AddChildContent("OP"));
+
+ Assert.Equal(
+ "https://avatars.tests.local/second.jpg",
+ cut.Find("img").GetAttribute("src"));
+ }
+
[Fact]
public async Task DialogHost_CompletesReplacedBackdropAndDisposedRequests()
{