Early bird: 30% off Dashral ProSee pricing

Dashral

Avatar UploadNew

A profile photo or logo with its upload: pick or drop a picture, crop it in a dialog, get a square file back.

$ pnpm dlx shadcn@latest add @dashral/avatar-upload

JPG, PNG or WebP, up to 5 MB. It's saved as a 512 × 512 picture.

Installation

$ pnpm dlx shadcn@latest add @dashral/avatar-upload

Adds the component, the components it builds on, its npm packages and the theme tokens it uses. Or by its full URL: https://dashral.com/r/avatar-upload.json

When to use

  • Someone's profile photo, or a workspace or company logo, in a settings form: pick or drop a picture, crop it, upload a small square file.

When not to use

  • Several files, or files that aren't pictures: File Dropzone.
  • A crop of another shape, such as a 3:1 cover: Image Cropper with cropImage.

Usage

defaultValue is the photo you have; onValueChange gets the cropped 512 px File to upload, or null when it's removed.

example.tsx
"use client"

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

export default function ProfilePhoto() {
  return (
    <AvatarUpload
      label="Profile photo"
      description="JPG or PNG, up to 5 MB."
      defaultValue="https://picsum.photos/id/64/400/400"
      maxSize={5 * 1024 * 1024}
      onValueChange={(file) => console.log("upload", file)}
    />
  )
}

Using the npm package? Import from @dashral/ui/components/avatar-upload instead, here and in the examples; the rest is the same.

Examples

Workspace logo

shape="square" with initials as the fallback, saved as a 256 px PNG to keep its transparency.

Shown in the sidebar and on invoices. A square PNG keeps its transparency.

API reference

AvatarUpload

PropTypeDefault
value / defaultValueFile | string | null

The picture now: a URL (from your server, say) or the File cropped last.

null
onValueChange(file: File | null) => void

The cropped picture, ready to upload, or null when it's removed.

—
acceptstring

Like the accept attribute; only pictures are taken.

"image/*"
maxSizenumber

Largest picture someone can pick, in bytes, before cropping.

—
shape"circle" | "square"

A circle for a person, a square for a logo; the file is square either way.

"circle"
fallbackReactNode

In the picture's place when there's none, e.g. initials.

a person icon
outputSizenumber

Width and height of the file, in pixels.

512
outputTypestring

"image/png" keeps a logo's transparency; "image/webp" is smaller.

"image/jpeg"
cropTitle / cropDescriptionReactNode

The crop dialog's title and the line under it.

—
namestring

Sends the cropped file with a form, as a file input of that name would. It's empty when nothing new was cropped: tell a removal apart with onValueChange.

—
disabled / required / id—
labelReactNode

Shown above, linked to the control.

—
descriptionReactNode

A hint under the control.

—
errorboolean | ReactNode

true shows the error style; a message also shows under the control.

—
labelActionReactNode

At the end of the label's row, e.g. a “Forgot password?” link or the current value.

—
withAsteriskboolean

Marks the label with an asterisk, e.g. alongside required.

false
classNames{ root, label, description, error, preview, actions }

Classes for each part.

—
classNamestring

Extra classes, merged with cn() so yours win.

—

Keyboard

KeysAction
TabFocus Upload (or Change) photo, then Remove
EnterorSpaceOpen the file picker
In the dialogAs on Image Cropper; Escape cancels

Accessibility

  • The buttons say what they do; the preview beside them is for the pointer and for a dropped file, so it's hidden from screen readers.
  • The whole control is role="group", named by its label and described by its description.
  • A refused file is said in an alert, with the reason, and the Upload button is described by it.
  • A picture that can't be opened or cropped says so in the dialog, and Apply waits until the picture has loaded.

Motion

  • A new picture fades in over the old one and a removed one fades out (--duration-standard), so the preview never blinks empty.

Related blocks

Whole sections built with Avatar Upload or made for the same job, ready to drop in. Free blocks come with their code; Pro blocks come with Dashral Pro.

  • Workspace brandingPro

    The workspace's name, logo, cover and colour, previewed live in the sidebar, the sign-in page, emails and invoices.

  • Profile settings

    A profile form with a photo cropped as it's picked, beside a card that shows how the profile looks to others as you type.

Browse all 65 blocks