shadcn Avatar Component with External Fallback Src
Wire a shadcn avatar fallback src URL into the Avatar, AvatarImage, and AvatarFallback primitives to show branded placeholders when profile images fail or are absent.
shadcn's Avatar is a thin wrapper around Radix UI's Avatar primitive. It renders `AvatarImage` when the src loads and falls back to `AvatarFallback` when it does not. By default `AvatarFallback` renders a text node — typically initials. Replacing that text node with a branded shadcn avatar fallback image URL gives you a more polished, on-brand fallback state without rebuilding the component.
This guide covers the Radix Avatar API, how to add an `<img>` inside `AvatarFallback` pointing to a fallback.pics avatar URL, how to generate the URL from user data, and how to handle the transition delay that Radix applies before showing the fallback.
Radix API
How Radix Avatar's three-state rendering model works
Radix's Avatar primitive has three states: `idle` (src not yet loaded), `loading`, and `error`. `AvatarImage` is only visible in the `loading` and `idle` states while the image loads. `AvatarFallback` becomes visible after a configurable `delayMs` — by default 600 ms — if the image has not loaded.
The `delayMs` prevents a flash of the fallback content on fast connections. It also means that on slow connections, users see nothing for 600 ms before the fallback appears. Lowering `delayMs` to 0 trades the flicker prevention for faster fallback rendering — the right choice depends on your median connection speed.
You cannot bypass the three-state model by setting `src` to an empty string. Radix treats an empty string as a valid (but immediately failing) src, which triggers the error state after the `delayMs`. Pass `undefined` explicitly when there is no src to skip the loading phase.
Implementation
Adding a fallback src image inside AvatarFallback
`AvatarFallback` accepts any React children. Replace the default initials text with an `<img>` tag pointing to the placeholder URL. The `<img>` inside `AvatarFallback` is a standard HTML element — it will load the placeholder URL normally and can itself have an `onError` handler as a last resort.
Set `width` and `height` on the inner `<img>` to match the `Avatar` dimensions to prevent a layout shift inside the fallback state.
// components/ui/user-avatar.tsx
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
function buildFallbackUrl(name: string, size: number) {
const text = name
.split(' ')
.map(n => n[0])
.join('')
.slice(0, 2)
.toUpperCase();
return `https://fallback.pics/api/v1/avatar/${size}?text=${text}`;
}
interface UserAvatarProps {
src?: string;
name: string;
size?: number;
className?: string;
}
export function UserAvatar({ src, name, size = 40, className }: UserAvatarProps) {
const fallbackUrl = buildFallbackUrl(name, size);
return (
<Avatar className={className} style={{ width: size, height: size }}>
<AvatarImage src={src} alt={name} />
<AvatarFallback delayMs={0}>
<img
src={fallbackUrl}
alt={name}
width={size}
height={size}
className="rounded-full object-cover"
/>
</AvatarFallback>
</Avatar>
);
} Color coding
Deterministic user colors from ID for branded avatar fallbacks
Use the same deterministic color approach as MUI avatars: hash the user ID, map to a palette index, and encode the hex pair into the URL. This ensures that the initials-based text fallback and the image fallback show the same color, creating a consistent visual identity per user across loading states.
const AVATAR_PALETTE = [
['7C3AED', 'FFFFFF'],
['0369A1', 'FFFFFF'],
['047857', 'FFFFFF'],
['B45309', 'FFFFFF'],
['BE123C', 'FFFFFF'],
['0F172A', 'F8FAFC'],
];
function hashId(id: string): number {
return id.split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) >>> 0, 0);
}
export function avatarUrl(userId: string, name: string, size = 40) {
const [bg, fg] = AVATAR_PALETTE[hashId(userId) % AVATAR_PALETTE.length];
const text = name.split(' ').map(n => n[0]).join('').slice(0, 2).toUpperCase();
return `https://fallback.pics/api/v1/avatar/${size}/${bg}/${fg}?text=${text}`;
} Loading delay
Tuning delayMs to prevent fallback flash on fast connections
Set `delayMs={300}` as a middle ground: fast enough that users on slow connections see the fallback quickly, but short enough to suppress the flicker on most broadband connections. Avoid setting it higher than 1000 ms — at that point users notice the empty Avatar state and assume the page is broken.
If you are rendering many avatars in a table or list, keep `delayMs` consistent. Inconsistent delays between avatar instances create a staggered reveal that looks like a bug rather than a design decision.
Group avatars
Applying fallback URLs in AvatarGroup stacks
AvatarGroup stacks multiple Avatars with overlapping rings. Use the same `UserAvatar` wrapper for each member. Fallbacks in a group are especially important because a missing avatar in the middle of a stack creates a gap that breaks the overlapping layout.
Set explicit `width` and `height` on every avatar in the group. Without them, a fallback image with different intrinsic dimensions can expand or contract the group container on load.
// Example: team member stack
function TeamAvatarGroup({ members }: { members: User[] }) {
return (
<div className="flex -space-x-2">
{members.map((m) => (
<UserAvatar
key={m.id}
src={m.avatarUrl}
name={m.name}
size={32}
className="ring-2 ring-background"
/>
))}
</div>
);
} Further reading
Avatar and profile image fallback resources
For MUI and Chakra UI avatar fallback patterns with the same deterministic color approach, see the companion guide. The avatar route at fallback.pics accepts a size parameter and renders a circular SVG with the initials centered.
https://fallback.pics/docs/
https://fallback.pics/placeholder-image-api/
https://fallback.pics/blog/mui-chakra-avatar-fallback-urls/
https://fallback.pics/blog/avatar-placeholder-generator-initials-colors-accessibility/ Key takeaways
What to standardize before shipping
- Place an `<img>` inside `AvatarFallback` pointing to the placeholder URL rather than rebuilding the Avatar component to add a fallback src prop.
- Pass `undefined` (not an empty string) as `src` when there is no profile image — Radix skips the loading phase entirely for `undefined`.
- Set `delayMs={300}` to balance flicker prevention on fast connections with quick fallback display on slow ones.
- Use deterministic colors from the user's ID so initials-based and image-based fallbacks share the same color scheme.
- Match `width` and `height` on the inner fallback `<img>` to the Avatar container size to prevent layout shifts inside the fallback state.
Production fallback layer
Use fallback.pics anywhere an image URL is accepted.
Start with one deterministic URL and standardize fallback behavior across your design system.