Avatar

A Base UI avatar whose image is clipped inside its hand-drawn outline, with hatched initials as the fallback.

Avatar
Portraits clipped inside a hand-drawn ring, with hatched initials when the image is missing.
Sizes
SDL
FallbackDrawn when no image loads
CHCHCH
Badge
ONOF
GroupHover to lift a face
CMAP
+3

Installation

npx shadcn@latest add https://sketchcn.chuwii.com/r/avatar.json

Styles

Avatar leans on these rules from sketch.css. The shadcn CLI installs the file with sketch-provider; copying the component by hand means copying them too.

@keyframes sketch-avatar-wobble {
  0% {
    transform: translateY(0) rotate(0deg);
  }

  35% {
    transform: translateY(-5px) rotate(-5deg);
  }

  60% {
    transform: translateY(-2px) rotate(4deg);
  }

  80% {
    transform: translateY(-4px) rotate(-2deg);
  }

  100% {
    transform: translateY(-3px) rotate(0deg);
  }
}

Read the whole file for the drawing variables, paper textures and reduced-motion rules.

Usage

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";

export function Profile() {
  return (
    <Avatar>
      <AvatarImage
        src="https://api.dicebear.com/9.x/notionists/svg?seed=Chuwii"
        alt="Chuwii"
      />
      <AvatarFallback>CH</AvatarFallback>
    </Avatar>
  );
}

Props

Avatar

Takes every Base UI Avatar.Root prop.

PropTypeDefaultDescription
size"sm" | "default" | "lg""default"Draws a 24px, 32px, or 40px ring.

AvatarImage

Takes every Base UI Avatar.Image prop. It is clipped to the drawn ring, so the photo edge wobbles with the ink.

AvatarFallback

Takes every Base UI Avatar.Fallback prop. Renders hatched shading behind whatever you put inside, usually initials.

AvatarBadge

Takes every span prop. Sits at the bottom-right corner and follows the avatar size.

AvatarGroup

Takes every div prop. Overlaps its avatars, haloes each one in the background colour, and wobbles the hovered face as it lifts. The wobble is skipped under prefers-reduced-motion.

AvatarGroupCount

Takes every div prop. A drawn circle for the overflow count, sized from the avatars in the group.

Examples

Profile row

CH
Chuwiicreator of sketchcn
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar";

export function ProfileRow() {
  return (
    <div className="flex items-center gap-3">
      <Avatar size="lg">
        <AvatarImage
          src="https://api.dicebear.com/9.x/notionists/svg?seed=Chuwii"
          alt="Chuwii"
        />
        <AvatarFallback>CH</AvatarFallback>
      </Avatar>
      <div className="flex flex-col">
        <span className="text-sm">Chuwii</span>
        <span className="text-muted-foreground text-xs">
          creator of sketchcn
        </span>
      </div>
    </div>
  );
}

Missing portrait

SK
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";

export function MissingPortrait() {
  return (
    <Avatar size="lg">
      <AvatarImage src="/nowhere.png" alt="Unknown teammate" />
      <AvatarFallback>SK</AvatarFallback>
    </Avatar>
  );
}

Team stack

MAP
+8
import {
  Avatar,
  AvatarFallback,
  AvatarGroup,
  AvatarGroupCount,
  AvatarImage,
} from "@/components/ui/avatar";

const MEMBERS = ["Mali", "Anan", "Pim"];

export function TeamStack() {
  return (
    <AvatarGroup>
      {MEMBERS.map((member) => (
        <Avatar key={member}>
          <AvatarImage
            src={\`https://api.dicebear.com/9.x/notionists/svg?seed=\${member}\`}
            alt={member}
          />
          <AvatarFallback>{member.charAt(0)}</AvatarFallback>
        </Avatar>
      ))}
      <AvatarGroupCount>+8</AvatarGroupCount>
    </AvatarGroup>
  );
}

Wobblier hand

SK
import { Avatar, AvatarFallback } from "@/components/ui/avatar";

export function ScribbledFallback() {
  return (
    <Avatar size="lg" className="[--sketch-roughness:2]">
      <AvatarFallback className="[--sketch-bg-hachure-gap:3] [--sketch-bg-hachure-angle:45]">
        SK
      </AvatarFallback>
    </Avatar>
  );
}

Animation

Inside an AvatarGroup, hovering a face lifts it out of the stack with the sketch-avatar-wobble keyframe from sketch.css. It ends raised, so the face stays up while the hover is held.

// on AvatarGroup
"*:data-[slot=avatar]:hover:z-10 *:data-[slot=avatar]:hover:[animation:sketch-avatar-wobble_450ms_ease-out_forwards]"

sketch.css already disables it under prefers-reduced-motion.

@media (prefers-reduced-motion: reduce) {
  [data-slot="avatar-group"] > [data-slot="avatar"]:hover {
    animation: none;
  }
}