RHF-MUI Components
NPM

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.

Warning

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.invalidFileType
  • FileUploadError.fileTooLarge
  • FileUploadError.limitExceeded

Accessibility

When using a custom upload button:

  • Ensure the button uses component="label".
  • Render the provided fileInput somewhere 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.

NameTypeDescription
fieldName*string
Name of the field registered with React Hook Form. This prop is required for all components.
control*UseFormControl
The control option yielded on calling the useForm hook.
registerOptionsRegisterOptions
React Hook Form validation rules. Useful when not using a schema validation library such as Yup or Joi.
requiredboolean
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.
onUploadErrorArray<{ 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.
acceptstring
The file types to accept in the file uploader component. Eg: image/* or .pdf,.docx.
multipleboolean
Allows selecting and uploading multiple files. When disabled, only a single file can be selected.
maxSizenumber
The maximum file size in bytes allowed for each uploaded file.
maxFilesnumber
Maximum number of files allowed. Any files exceeding this limit will be rejected. Files provided via existingFiles are also counted toward the limit.
disabledboolean
Disables the component and prevents user interaction.
dropZonePropsBoxProps / ({ 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.
disableDragAndDropboolean
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.
existingFilesArray<{ 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.
existingFileListPropsBoxProps
Props applied to the wrapper Box element that contains files provided via existingFiles.
uploadedFileListPropsBoxProps
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.
labelReactNode
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.
showLabelAboveFormFieldboolean
Render form label above the form field in FormLabel component.
formLabelPropsFormLabelProps
FormLabelProps to customise FormLabel component for a field. Multiple fields can be configured using the ConfigProvider component.
hideLabelboolean
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.
hideErrorMessageboolean
A flag to prevent replacement of helper text of a field by the errorMessage when the validation is triggered.
helperTextReactNode
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.
formHelperTextPropsFormHelperTextProps
FormHelperTextProps to customise FormHelperText component for a field. Multiple fields can be configured using the ConfigProvider component.
fullWidthboolean
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.