Skip to content
Drivn
5 min read

Next.js Avatar — Server-Rendered, Zero JS

Add a Next.js Avatar that renders on the server with zero JavaScript. Drivn ships a hook-free, zero-dependency component with image and initials fallback.

An avatar is a tiny piece of UI with an outsized presence — it appears in the header, the comment list, the team page, and every notification row. In a Next.js app most of those surfaces are server components, so the avatar should render on the server too. It holds no state and reacts to nothing: it shows an image when one exists and falls back to initials when it does not. There is no reason to ship JavaScript for it, and no reason to pull a runtime UI package into your bundle just to draw a circle.

Drivn's Avatar was built for exactly this. It is a plain function that renders a single rounded div with a gradient background, then chooses between an <img> and a span of uppercase initials based on whether a src is provided. There is no 'use client' directive, no useState, no effect, and no browser-only API in the render path. Drop it into any App Router page, layout, or server component and Next.js streams it as part of the initial HTML.

This guide installs Drivn in a Next.js 16 project, renders an Avatar inside a server component, wires up the initials fallback, optimizes the image with next/image, and tunes the size presets and gradient. Every snippet comes from the component source the CLI installs. For the full API see the Avatar docs; for the head-to-head see Drivn vs shadcn/ui Avatar.

Install in a Next.js 16 project

Drivn installs through a small CLI that writes component source files directly into your repository — there is no runtime npm package and no version to keep upgrading. Open a terminal at the root of your Next.js 16 project and run npx drivn add avatar. The CLI prompts for an install directory, defaulting to src/components/ui/, then copies avatar.tsx into place. The Avatar pulls in no peer dependency at all — it is pure React and Tailwind, so there is nothing else to install and no icon library to add. The CLI reference documents every flag, including targeting a custom path or adding several components in one command. After install you own the file; future Drivn releases never overwrite it, and you can edit the internals without forking a package.

1# from the root of your Next.js 16 project
2npx drivn add avatar

Render inside a Server Component without 'use client'

App Router pages are server components by default, so any client-side hook inside one fails at build time. Drivn's Avatar has no hooks to fail — it is a pure function with no 'use client' directive, no state, and no effect. Render it directly inside a server page and Next.js streams the markup as static HTML, shipping zero JavaScript for the avatar itself. Pass src for the image and fallback for the initials that show while no image is set or if the URL is missing. The installation guide covers project bootstrapping; the Avatar docs list every prop the component accepts.

1// app/page.tsx — server component
2import { Avatar } from '@/components/ui/avatar'
3
4export default function Page() {
5 return (
6 <header className="flex items-center gap-3 py-6">
7 <Avatar src="/users/jane.jpg" alt="Jane Doe" fallback="JD" />
8 <span className="text-sm font-medium">Jane Doe</span>
9 </header>
10 )
11}

Show initials when there is no image

Most user records have a name long before they have an uploaded photo, so the initials fallback is the common case, not the edge case. When src is omitted or empty, the Avatar renders a span containing fallback?.slice(0, 2).toUpperCase() — the first two characters of whatever string you pass, uppercased. Pass a two-letter code like "JD" directly, or pass a full name and let the slice trim it. The fallback sits on the component's gradient background, which runs from the primary to the secondary token, so initials read clearly without any extra styling. Because this logic lives in your repo after install, swapping the slice for a multi-word initial extractor is a small source edit. The Avatar examples show initials, groups, and status badges in context.

1import { Avatar } from '@/components/ui/avatar'
2
3// No src — renders "JD" on the gradient background
4<Avatar fallback="Jane Doe" />
5
6// Explicit two-letter code
7<Avatar fallback="AB" size="lg" />

Optimize the image with next/image

The Avatar ships with a plain <img> so it works in any React project without a framework dependency. In a Next.js app you usually want the optimizer instead, and because you own the source after install, switching is a small edit rather than a config flag. Open src/components/ui/avatar.tsx, import Image from next/image, and replace the <img> branch with an <Image fill /> — the container is already relative and overflow-hidden, so fill anchors to it correctly. Add a sizes value matching your largest preset (64px for xl) so Next.js serves an appropriately scaled file. The rest of the component, including the initials fallback, stays exactly as it was.

1// src/components/ui/avatar.tsx — swap the <img> branch
2import Image from 'next/image'
3
4{src ? (
5 <Image
6 src={src}
7 alt={alt ?? ''}
8 fill
9 sizes="64px"
10 className="object-cover"
11 />
12) : (
13 <span className="font-medium text-primary-foreground">
14 {fallback?.slice(0, 2).toUpperCase()}
15 </span>
16)}

Size presets and the gradient background

The size prop accepts four presets — sm, md, lg, xl — mapped to width, height, and text-size pairs in the component's styles.sizes object, defaulting to md. The base styles set rounded-full, overflow-hidden, and a bg-gradient-to-br from-primary to-secondary that shows through behind initials and during image load. Because the className prop merges last through cn, you can override the radius for a squircle, swap the gradient for a flat token color, or add a ring without touching the source. To restyle every avatar at once, edit styles.base directly in src/components/ui/avatar.tsx. The theming page maps the primary and secondary tokens the gradient reads from.

1// src/components/ui/avatar.tsx — size presets
2const styles = {
3 base: cn(
4 'relative inline-flex items-center justify-center',
5 'rounded-full overflow-hidden',
6 'bg-gradient-to-br from-primary to-secondary'
7 ),
8 sizes: {
9 sm: 'w-8 h-8 text-xs',
10 md: 'w-10 h-10 text-sm',
11 lg: 'w-12 h-12 text-base',
12 xl: 'w-16 h-16 text-lg',
13 },
14}
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 Avatar is a pure function that renders a div with either an image or a span of initials — it holds no state, runs no effect, and uses no browser-only API. That means no 'use client' directive and zero JavaScript shipped for the avatar itself. It renders as a server component inside any App Router page or layout, and the HTML that reaches the browser already contains the image tag or the initials.

When the src prop is omitted or empty, the component renders fallback?.slice(0, 2).toUpperCase() inside a span — the first two characters of the fallback string, uppercased, sitting on the gradient background. Pass a two-letter code like "JD" directly, or pass a full name and the slice trims it to the first two letters. Because the logic lives in your repo, you can replace it with a smarter multi-word initial extractor in a few lines.

Yes, with a small source edit. The Avatar ships a plain <img> for framework portability, but the container is already relative and overflow-hidden, so you can import Image from next/image and replace the image branch with <Image fill sizes="64px" className="object-cover" />. The initials fallback and every other prop stay unchanged. Since you own the file after install, the edit is permanent and never overwritten by an upgrade.

None. The component imports only React and the local cn class-merge helper — there is no Radix primitive, no image-loading library, and no lucide-react requirement because it renders no icon. The CLI writes a single self-contained .tsx file into your project, so your bundle grows by the size of that one file and nothing else, whether you keep the plain <img> or switch to next/image.