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.