Xzero UI
Pro library

Avatar uploadPro

Click or drop a photo onto the avatar to crop it in place with drag, pinch and zoom. It uploads through a progress ring and settles, with clear errors and retry.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/avatar-upload
Live · keyboard ready

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/avatar-upload

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { AvatarUpload } from "@/components/xzero/avatar-upload/avatar-upload";

export function ProfilePhoto({ user }: { user: User }) {
  return (
    <AvatarUpload
      name={user.name}
      src={user.avatarUrl}
      onUpload={async (image, { report, signal }) => {
        const { uploadUrl, publicUrl } = await api.createAvatarUpload();
        await putWithProgress(uploadUrl, image, { onProgress: report, signal });
        await api.saveAvatar(publicUrl);
        return publicUrl;
      }}
      onRemove={() => api.removeAvatar()}
    />
  );
}

API reference

AvatarUpload

A profile photo control. Click or drop an image on the avatar and the avatar itself grows into a circular crop stage, with the rest of the photo dimmed around the mask. Drag to reposition, scroll, pinch or use the slider to zoom, then save: the circle shrinks back with the cropped photo inset inside a progress ring, and when the upload resolves the ring closes and lets go as the photo settles to full size. Too large, wrong type and failed uploads are explained in place, with retry. Initials show when there is no photo.

PropTypeDefaultDescription
name (required)string–Person's name, used for the initials fallback.
srcstring–Current photo URL. Changing it replaces the shown photo.
onUpload (required)(image: Blob, context: { report: (progress: number) => void; signal: AbortSignal; original: File }) => Promise<string | void>–Receives the cropped square image. Call report with 0 to 1 as bytes go out to fill the ring; until you do, the ring turns without a value. Resolve with the stored URL, or with nothing to keep showing the local crop. Reject to show the error message and a Try again button. signal aborts when the person cancels.
onRemove() => void–Called after Remove. Without it the Remove button is hidden.
onError(error: { code: "type" | "size" | "decode" | "upload"; message: string }) => void–Called whenever an error message is shown, for logging or analytics.
acceptstring[]["image/png", "image/jpeg", "image/webp"]Accepted MIME types. The default hint lists them.
maxSizenumber5242880Largest file in bytes, 5 MB by default. Checked before the image is decoded.
sizenumber88Avatar diameter in px while not cropping. The crop circle is 184px.
outputSizenumber512Side of the square image passed to onUpload, in px. PNGs stay PNG to keep transparency; everything else becomes JPEG at 92% quality.
labelstring"Profile photo"Heading beside the avatar and the group's accessible name.
hintReactNode–Helper copy. Defaults to the accepted types and the size limit, such as PNG, JPG or WebP, up to 5 MB.
disabledboolean–Blocks picking, dropping and removing.
classNamestring–Class on the root element.
styleCSSProperties–Inline styles on the root element.
refRef<AvatarUploadHandle>–open() shows the file picker; selectFile(file) runs a file through the same checks as a pick or drop and opens the cropper, for your own buttons, paste handlers or camera capture.

Keyboard interactions

KeysAction
TabMoves to the avatar, then Upload photo or Replace photo, then Remove. While cropping: the crop area, zoom out, the zoom slider, zoom in, Save photo, Cancel.
Enter / SpaceOn the avatar, opens the file picker.
Arrow keysIn the crop area, move the photo by 2% of the circle; hold Shift for 10%.
+ / -In the crop area, zoom in or out.
0In the crop area, reset zoom and position.
EnterIn the crop area, save and upload.
EscapeIn the crop area, cancel and return to the current photo.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Also in editors

Guidance for AI tools

Avatar upload: Click or drop a photo onto the avatar to crop it in place with drag, pinch and zoom. It uploads through a progress ring and settles, with clear errors and retry. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components