Components
Avatar
A member's picture, with the legacy default silhouette behind it. A missing source and a URL that 404s land on the same fallback.
Usage
src takes an OSS object key or an absolute URL — keys resolve against the
bucket with x-oss-process resize parameters, absolute URLs pass through
untouched. Not next/image: avatars arrive with server-side resizing already
in the URL, so a second optimizer pass adds latency for nothing. Shape and
size come from className.



import { Avatar } from "@/components/base/avatar";
<Avatar src={member.avatar} width={64} className="size-8 rounded-full" />Fallback
A broken avatar is common enough — a deleted OSS object, a third-party URL
that rots — that the component is a client component purely for onError. The
failure is keyed by URL rather than a bare boolean, so a later src gets its
own chance to load instead of inheriting the previous one's failure. The
default is a white silhouette, inverted on the light theme — and only when it
is actually the one showing, since inverting a real photograph would be its
own kind of broken.
Sizing
width feeds the OSS resize parameter, not the layout: pass roughly twice the
rendered size, and let className set the box.