Skip to content
Drivn logoDrivn
6 min read

Drivn vs shadcn/ui — Avatar Component Compared

Side-by-side comparison of Drivn and shadcn/ui React Avatar — API shape, image fallback, runtime dependencies, size variants, and initials rendering.

Avatar components have one job — render a profile picture, and fall back gracefully when that picture is unavailable. Drivn and shadcn/ui both ship the Avatar as copy-paste source code, but they diverge in three ways: how many components you import, where the fallback logic lives, and which event or condition triggers the fallback state.

shadcn's Avatar is built on @radix-ui/react-avatar. It exports three components — Avatar, AvatarImage, and AvatarFallback — and delegates image-error detection to the Radix primitive. When the image URL fails or returns a 404, the Radix layer fires an internal status event and renders the AvatarFallback slot. You compose the two slots yourself and pass any initials or icon as children of the fallback.

Drivn's Avatar is a single component with four props: src, alt, fallback, and size. The fallback logic is a conditional render — when src is provided the component renders an <img>; when it is absent a <span> displays the first two characters of the fallback string, uppercased. There is no Radix primitive, no image-error event, and no slot composition. Four named sizes — sm, md, lg, and xl — are built in; shadcn has none.

This comparison covers API shape, image loading behavior, fallback rendering, size variants, runtime dependencies, and the use cases where each model fits best.

Side-by-side comparison

FeatureDrivnshadcn/ui
Runtime UI dependenciesNone (React + Tailwind)@radix-ui/react-avatar
API shapeSingle component + props3 named exports (Avatar, AvatarImage, AvatarFallback)
Fallback triggerConditional render (src absent)Image onError event (Radix managed)
Auto-fallback on 404
Built-in size variants4 (sm, md, lg, xl)None (className-driven)
Initials truncationAuto (first 2 chars, uppercased)Manual (children of AvatarFallback)
Fallback backgroundGradient (from-primary to-secondary)bg-muted (neutral)
Source size≈35 lines, no primitiveWrapper + Radix primitive
LicenseMITMIT
Copy-paste install

API side-by-side

shadcn/ui's Avatar is three components composed inside each other: an Avatar root, an AvatarImage for the photo, and an AvatarFallback for the initials or placeholder. You write all three every time you render an avatar — for a comment thread with fifty rows, that is 150 component calls plus any icon or text inside AvatarFallback. Drivn's Avatar is a single component: you pass src, alt, fallback, and size. One call, four props, done.

The tradeoff is composability versus ergonomics. shadcn's slot model lets you put any JSX inside the fallback without prop surgery; Drivn's prop model keeps the call site clean at the cost of a one-line edit to the component file when you want a custom fallback other than initials. For most applications — comment threads, user tables, account headers — the initials fallback is all you need, so the prop API saves repetitive markup throughout the codebase.

1// shadcn/ui — three imports, nested slots
2import {
3 Avatar,
4 AvatarImage,
5 AvatarFallback,
6} from '@/components/ui/avatar'
7
8<Avatar>
9 <AvatarImage src="/user.jpg" alt="Jane Doe" />
10 <AvatarFallback>JD</AvatarFallback>
11</Avatar>
12
13// Drivn — one import, prop API
14import { Avatar } from '@/components/ui/avatar'
15
16<Avatar
17 src="/user.jpg"
18 alt="Jane Doe"
19 fallback="JD"
20 size="md"
21/>

Image fallback behavior

The most meaningful difference between the two libraries is what happens when the image fails to load. shadcn/ui's Radix primitive listens to the browser's onError event on the underlying <img> element. When the image returns a 404, the network is unavailable, or the URL is malformed, Radix transitions from the image slot to the fallback slot automatically, without any prop change. You always see either the image or the fallback, never a broken image icon.

Drivn's Avatar uses a conditional render keyed on whether src is truthy. If src is an empty string, undefined, or null, the fallback renders immediately. If src has a value — even a URL that returns 404 — the <img> element renders and the browser handles the error normally. In practice a broken image icon appears if the URL resolves to nothing. The fix is to validate image URLs before passing them as src, or add a one-line onError handler inside avatar.tsx after install. Because the source lives in your repo, you own that change permanently without waiting for a library release.

Built-in size variants

shadcn/ui's Avatar has no built-in sizes. The root container defaults to h-10 w-10 (40 px), and any other size requires overriding the className at the call site. Drivn ships four named sizes — sm (32 px), md (40 px, default), lg (48 px), and xl (64 px) — as entries in the const styles.sizes object. Passing size="lg" to a comment author avatar and size="sm" to a data table cell requires no className at the call site.

Adding a custom size to Drivn is one line in avatar.tsx: add a key to styles.sizes with a width and height Tailwind class, and the keyof typeof styles.sizes type picks it up immediately. The theming guide describes how to add a CSS custom property if your brand needs a size that the four defaults do not cover. For teams building tables, lists, and headers that render the same Avatar at different scales, the built-in size variants eliminate a className-per-callsite pattern that accumulates quickly.

Initials and fallback content

shadcn/ui's AvatarFallback renders any children you pass — a two-letter string, an icon component, or a full SVG. You control the content entirely, but you also write it at every call site. Drivn's fallback is prop-driven: pass a string as fallback, and the component renders fallback.slice(0, 2).toUpperCase() automatically. Passing "Jane Doe" renders "JA"; passing "JD" renders "JD". The gradient background behind the initials uses the --color-primary and --color-secondary design tokens, so the fallback looks on-brand without a className.

If you need a non-initials fallback — an icon, an anonymous silhouette, a question mark — the path with Drivn is a one-line edit to avatar.tsx to replace the <span> with conditional JSX. With shadcn you pass the icon as children with no edit needed. This is the one area where shadcn's slot model wins on composability. For teams whose fallback is always initials, the prop API is faster at every call site. For teams with mixed fallback content, shadcn's children model is more flexible.

Customization

Both libraries put the Avatar source directly in your repo, so both invite the same category of customizations. The shape of the customization differs. shadcn adds sizing and shape overrides via className at the call site. Drivn's const styles object is a plain record of Tailwind strings — styles.sizes.sm, styles.sizes.md, styles.base — all indexed by keyof typeof types. Adding a new size or changing the base border radius is a literal string edit.

For design token integration, Drivn's Avatar uses from-primary and to-secondary gradient classes. Both map to CSS custom properties in globals.scss, so rebranding the fallback background is a token edit that propagates to every Avatar instance automatically. The theming docs cover which tokens are involved. For adding a ring, a border, or a status dot overlay, the Avatar's outer div has relative and overflow-hidden applied — stack a positioned <span> as a sibling wrapped in a relative container. The avatar examples page shows a working status dot pattern.

Get started

Install Drivn in one command

Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.

npx drivn@latest create

Requires Node 18+. Works with npm, pnpm, and yarn.

Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →

Frequently asked questions

No. The Drivn Avatar uses a conditional render — it renders <img> when src is truthy and the initials span when it is not. A 404 URL is still truthy, so the browser renders a broken image. To add auto-fallback behavior, open src/components/ui/avatar.tsx after install and add onError={(e) => { (e.target as HTMLImageElement).style.display = "none" }} to the <img> element, then conditionally show the fallback span. Because the source lives in your repo, this is a permanent one-time edit.

Edit src/components/ui/avatar.tsx and replace the fallback <span> with your icon. The component uses a simple conditional render — {src ? <img ... /> : <span>...</span>} — so the else branch is the fallback content. Swap the span for <UserRound className="w-1/2 h-1/2 text-primary-foreground" /> or any other icon component. The gradient background from the outer container applies automatically.

Yes — wrap the Avatar in a relative container and add a positioned sibling element. A common pattern is <div className="relative inline-flex"><Avatar .../><span className="absolute bottom-0 right-0 w-3 h-3 bg-green-500 rounded-full border-2 border-background" /></div>. The outer div positions the dot relative to the avatar circle; the border-background class gives a ring effect that matches both light and dark themes automatically.

Open src/components/ui/avatar.tsx after install and add a key to the styles.sizes object — for example "2xl": "w-20 h-20 text-xl". The size prop type is derived from keyof typeof styles.sizes, so TypeScript autocompletes the new size at every call site immediately with no other change. The new variant is available across the entire codebase as soon as you save the file.