RHFFileUploader
RHFFileUploader is a flexible file upload component for React Hook Form. It supports:
- Single and multiple file uploads
- Drag-and-drop uploads
- Existing server-side files
- File type validation
- File size validation
- Upload count limits
- Custom upload button rendering
- Custom file item rendering
- Custom drop zone styling and behavior
Usage
import RHFFileUploader, { RHFFileUploaderProps, FileUploadError } from '@nish1896/rhf-mui-components/mui/file-uploader';Here is a basic example of the component configured to accept a single file:
<RHFFileUploader
fieldName="resume"
control={control}
/>A file object has the following properties:
{
lastModified: 1738870405923,
lastModifiedDate: "Fri Feb 07 2025 01:03:25 GMT+0530 (India Standard Time)",
name: "Picture.png",
size: 860146,
type: "image/png",
webkitRelativePath: ""
}When uploading files through an API, send them in a FormData object.
async function onFormSubmit(formValues: FormSchema) {
const formData = new FormData();
const { resume, pictures } = formValues;
/* resume can be null, so handle the null check */
resume && formData.append('resume', resume);
Array.isArray(pictures) && pictures.forEach(file => {
formData.append('pictures', file);
});
await sendFormData(url, formData);
}On a Node.js Express server, you can handle this incoming data with the Multer middleware. See:
Multiple Files & Validation
RHFFileUploader can be configured to:
- Accept multiple files
- Restrict file types using
accept - Limit file size using
maxSize - Limit uploaded file count using
maxFiles - Handle upload validation errors
- Render custom upload buttons
- Render custom file items
<RHFFileUploader
fieldName="pictures"
control={control}
multiple
accept="image/*"
maxFiles={3}
maxSize={5 * 1024 * 1024}
onUploadError={(errors) => {
alert(`${errors.length} file(s) were rejected.`);
}}
/>Drag and Drop
Drag-and-drop uploads are enabled by default.
<RHFFileUploader
fieldName="documents"
control={control}
/>Disable drag-and-drop:
<RHFFileUploader
fieldName="documents"
control={control}
disableDragAndDrop
/>Custom Drop Zone
Customize the drop zone appearance and behavior using dropZoneProps.
<RHFFileUploader
fieldName="documents"
control={control}
dropZoneProps={({ isDragging, disabled, error }) => ({
sx: {
borderColor: error
? 'error.main'
: isDragging
? 'primary.main'
: 'grey.400',
opacity: disabled ? 0.5 : 1
}
})}
/>Where:
type RHFFileUploaderDropZoneState = {
/** Whether a file is currently being dragged over the drop zone. */
isDragging: boolean;
/** Whether the uploader is disabled. */
disabled: boolean;
/** Whether the uploader is currently displaying a validation error. */
error: boolean;
};Existing Files
Use existingFiles to display files that have already been uploaded and stored on the server.
<RHFFileUploader
fieldName="documents"
control={control}
existingFiles={[
{
name: 'contract.pdf',
url: '/uploads/contract.pdf',
size: 102400
},
{
name: 'invoice.pdf',
url: '/uploads/invoice.pdf'
}
]}
/>Existing files are rendered separately from newly uploaded files and are counted toward the maxFiles limit.
Custom Upload Button
Use renderUploadButton to provide a fully custom upload control.
<RHFFileUploader
fieldName="documents"
control={control}
renderUploadButton={fileInput => (
<Button
component="label"
variant="contained"
startIcon={<UploadFileIcon />}
>
Upload Documents
{fileInput}
</Button>
)}
/>Custom File Rendering
Use renderFileItem to customize how newly uploaded files are displayed.
<RHFFileUploader
fieldName="documents"
control={control}
multiple
renderFileItem={({ file, index, removeFile }) => (
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Typography>
{index + 1}. {file.name}
</Typography>
<IconButton onClick={removeFile}>
<DeleteIcon />
</IconButton>
</Stack>
)}
/>The renderer receives:
{
file: File;
index: number;
removeFile: (
event: MouseEvent<HTMLButtonElement>
) => void;
}Custom Existing File Rendering
Use renderExistingFileItem to customize how server-side files are displayed.
<RHFFileUploader
fieldName="documents"
control={control}
existingFiles={[
{
name: 'contract.pdf',
url: '/uploads/contract.pdf'
}
]}
renderExistingFileItem={({ file, index }) => (
<Link
href={file.url}
target="_blank"
rel="noopener noreferrer"
>
{index + 1}. {file.name}
</Link>
)}
/>The renderer receives:
{
file: {
name: string;
url: string;
size?: number;
};
index: number;
}Value Change Events
Depending on your use case, you can use customOnChange to gain complete control over file
validation and form state updates, or use onValueChange to respond to file uploads, removals, and
clear actions after the field value has changed.
customOnChange
Use customOnChange when you need full control over how uploaded files are processed before updating the React Hook Form field value.
Unlike onValueChange, which is a notification callback, customOnChange overrides the default update behavior.
When using customOnChange, you are responsible for calling rhfOnChange manually. If rhfOnChange is not called, the form value will not be updated.
customOnChange?: ({
rhfOnChange,
newValue,
event
}) => void;Example: Restrict Total File Size
<RHFFileUploader
fieldName="documents"
control={control}
multiple
customOnChange={({ rhfOnChange, newValue }) => {
const files = Array.isArray(newValue)
? newValue
: newValue
? [newValue]
: [];
const totalSize = files.reduce(
(sum, file) => sum + file.size,
0
);
const maxTotalSize = 20 * 1024 * 1024; // 20 MB
if (totalSize > maxTotalSize) {
alert('Total file size cannot exceed 20 MB');
return;
}
rhfOnChange(newValue);
}}
/>Example: Filter Files Before Saving
<RHFFileUploader
fieldName="documents"
control={control}
multiple
customOnChange={({ rhfOnChange, newValue }) => {
const files = Array.isArray(newValue)
? newValue.filter(file => !file.name.startsWith('temp_'))
: newValue;
rhfOnChange(files);
}}
/>onValueChange
onValueChange is called whenever:
- Files are uploaded
- Files are removed
- The field is cleared
<RHFFileUploader
fieldName="documents"
control={control}
onValueChange={({ newValue, event }) => {
console.log(newValue);
}}
/>Upload Validation Errors
Use onUploadError to handle validation failures.
<RHFFileUploader
fieldName="documents"
control={control}
accept="image/*"
maxSize={2 * 1024 * 1024}
maxFiles={3}
onUploadError={errors => {
/* [{ file, errors: [FileUploadError.sizeExceeded] }] */
console.log(errors);
}}
/>Possible error values:
FileUploadError.invalidFileTypeFileUploadError.fileTooLargeFileUploadError.limitExceeded
Accessibility
When using a custom upload button:
- Ensure the button uses
component="label". - Render the provided
fileInputsomewhere inside the label. - Add
tabIndex={-1}to custom buttons when appropriate to avoid duplicate focus targets.
File dialogs can only be opened through direct interaction with a file input or its associated label element.
Examples
API
The RHFFileUploader accepts the following props.
Props marked with * are required.
| Name | Type | Description |
|---|---|---|
fieldName* | string | Name of the field registered with React Hook Form. This prop is required for all components. |
control* | UseFormControl | |
registerOptions | RegisterOptions | React Hook Form validation rules. Useful when not using a schema validation library such as Yup or Joi. |
required | boolean | Indicates that the field is mandatory by adding an asterisk symbol (*) to the formLabel. This visual cue helps users quickly identify required fields in the form. |
customOnChange | ({ rhfOnChange, newValue, event }) => void | Custom change handler that overrides the default file upload behavior. Receives the updated value, the triggering event, and rhfOnChange. Use this to perform custom validation or transform files before updating the form state. When provided, you are responsible for calling rhfOnChange manually. |
onValueChange | ({ newValue: File / File[] / null, event: ChangeEvent / DragEvent / MouseEvent }) => void | Optional callback fired when files are uploaded, removed, or cleared. Receives the updated field value and the event that triggered the change. |
onUploadError | Array<{ file, errors }> => void | Callback function that returns validation errors grouped by rejected file. |
onBlur | (event: FocusEvent) => void | Callback function that returns the blur event when the file uploader component loses focus. Added in v4.2.0. |
accept | string | The file types to accept in the file uploader component. Eg: image/* or .pdf,.docx. |
multiple | boolean | Allows selecting and uploading multiple files. When disabled, only a single file can be selected. |
maxSize | number | The maximum file size in bytes allowed for each uploaded file. |
maxFiles | number | Maximum number of files allowed. Any files exceeding this limit will be rejected. Files provided via existingFiles are also counted toward the limit. |
disabled | boolean | Disables the component and prevents user interaction. |
dropZoneProps | BoxProps / ({ isDragging, disabled, error }) => BoxProps | Props applied to the drag-and-drop wrapper. Accepts either a BoxProps object or a callback that receives the current drop-zone state and returns BoxProps dynamically. |
disableDragAndDrop | boolean | Disables drag-and-drop uploads and only allows file selection through the upload button. |
renderUploadButton | (fileInput: ReactNode) => ReactNode | Custom render function to replace the default upload button in the file uploader component. Refer to the example for more details. |
existingFiles | Array<{ name, url, size }> | List of files that already exist on the server. These files are rendered separately from newly uploaded files and count toward the maxFiles limit. |
renderExistingFileItem | ({ file, index }) => ReactNode | Custom renderer for each file provided via existingFiles. |
existingFileListProps | BoxProps | Props applied to the wrapper Box element that contains files provided via existingFiles. |
uploadedFileListProps | BoxProps | Props applied to the wrapper Box element that contains files selected during the current session. |
renderFileItem | ({ file, index, removeFile }) => ReactNode | Custom renderer for files selected during the current session. Receives the file, its index, and a helper function to remove the file. Refer to the example for more details. |
label | ReactNode | The text to render in the FormLabel component. By default, the value of fieldName is transformed (e.g., "firstName" to "First Name") using the fieldNameToLabel function. |
showLabelAboveFormField | boolean | Render form label above the form field in FormLabel component. |
formLabelProps | FormLabelProps | FormLabelProps to customise FormLabel component for a field. Multiple fields can be configured using the ConfigProvider component. |
hideLabel | boolean | Hides the FormLabel component if you don’t want to display the default form label component or prefer to render a fully custom label instead. |
renderError | (error: FieldError) => ReactNode | Custom renderer for the React Hook Form field error. Receives the current field error and returns the content to display, such as error.message or a custom React element in the HelperText component.Added in v4.1.0. |
hideErrorMessage | boolean | A flag to prevent replacement of helper text of a field by the errorMessage when the validation is triggered. |
helperText | ReactNode | The content to display within the FormHelperText component below the field. If the field validation fails, this content will be overridden by the corresponding error message. |
formHelperTextProps | FormHelperTextProps | FormHelperTextProps to customise FormHelperText component for a field. Multiple fields can be configured using the ConfigProvider component. |
fullWidth | boolean | Set the width of the file uploader component to 100%. |
customIds | { field, label, helperText, error } | Overrides the default field, label, helper text, and error IDs used for accessibility. |
Source Code
View the full implementation of this component on GitHub.

