Image/avatar upload field: a circular or square preview area with select/remove controls, wired to an async uploader. Defaults to accepting images.
Use AvatarFileInput when the uploaded single file is an image shown as an avatar-style preview (profile picture, logo). The empty state shows an icon; once a file is set, its preview replaces it.
@uxf/form form: use @uxf/form/avatar-file-input instead — it wires react-hook-form (useController) and reads the form context. @uxf/ui/avatar-file-input is the controlled primitive you drive with value / onChange.FileInput.Dropzone.import { FileResponse } from "@uxf/core/types";
import { AvatarFileInput } from "@uxf/ui/avatar-file-input";
import { useState } from "react";
// your uploader: send the file to storage, resolve with the stored file
declare function uploadFile(file: File): Promise<FileResponse>;
function Example() {
const [value, setValue] = useState<FileResponse | null>(null);
return (
<AvatarFileInput
label="Profile picture"
name="avatar"
onChange={setValue}
onUploadFile={uploadFile}
value={value}
/>
);
}
| Prop | Type | Default | Description |
|---|---|---|---|
value | FileResponse | null | — | Required. Currently selected file (controlled). |
onChange | (value: FileResponse | null, event?) => void | — | Required. Called with the uploaded file, or null when removed. |
onUploadFile | (file: File, options?: UploadOptions) => Promise<FileResponse> | — | Required. Uploads the picked file; resolves to the stored file. |
name | string | — | Required. Field name. |
onUploadError | (err: unknown) => void | — | Called when the upload throws or the file exceeds maxFileSize. |
accept | string | "image/*" | Native accept attribute. |
maxFileSize | number | — | Max size in bytes; larger files reject before uploading. |
variant | "default" | "square" | "default" | "default" is a circle, "square" has square corners. |
icon | IconName | "cloud" | Icon shown in the empty state. |
label | ReactNode | — | Field label. |
helperText | ReactNode | — | Helper / error text under the field. |
hiddenLabel | boolean | false | Keep the label for a11y but hide it visually. |
selectFileLabel | ReactNode | "Upload image" | Label of the default select button. |
removeFileLabel | ReactNode | "Remove avatar" | Label of the default remove button. |
customControls | (args: { onSelectFile: MouseEventHandler; onRemoveFile: MouseEventHandler }) => ReactNode | — | Replace the default buttons with your own controls. |
isDisabled | boolean | false | Disable the field. |
isReadOnly | boolean | false | Read-only. |
isInvalid | boolean | false | Invalid styling and aria-invalid. |
isRequired | boolean | false | Mark the field required. |
isFocused | boolean | — | Force the focused styling. |
onFocus / onBlur | FocusEventHandler<HTMLInputElement> | — | Focus handlers. |
id | string | auto (useId) | Input id; auto-generated if omitted. |
form | string | — | Associate the input with a form by id. |
className | string | — | Extra class on the root element. |
default (circular preview) and square.src is built from the file itself via getFileUrl (@uxf/core/utils/file), resolving to a relative /upload/... path — no UiContext needed.customControls to render your own buttons; it receives onSelectFile and onRemoveFile handlers.<AvatarFileInput
customControls={({ onRemoveFile, onSelectFile }) => (
<>
<button onClick={onSelectFile} type="button">
Change
</button>
<button onClick={onRemoveFile} type="button">
Remove
</button>
</>
)}
name="avatar"
onChange={setValue}
onUploadFile={uploadFile}
value={value}
/>
Client component ("use client").
Import the required stylesheets once in your global CSS:
@import url("@uxf/ui/css/label.css");
@import url("@uxf/ui/css/form-component.css");
@import url("@uxf/ui/css/avatar.css");
@import url("@uxf/ui/css/button.css");
@import url("@uxf/ui/css/icon.css");
@import url("@uxf/ui/css/avatar-file-input.css");
Also requires the global @uxf/ui token layer, set up once per app — see @uxf/ui setup.