MUI Components
NPMGithub

MUIFileUploader

MUIFileUploader is a flexible file upload component which 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 MUIFileUploader, { MUIFileUploaderProps } from '@nish1896/mui-components/mui/file-uploader';

Here is a basic example of the component configured to accept a single file:

const [file, setFile] = useState<File | null>(null);

<MUIFileUploader
  fieldName="resume"
  value={file}
  onValueChange={({ newValue }) => setFile(newValue)}
/>

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#

MUIFileUploader 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
const [files, setFiles] = useState<File[] | null>(null);

<MUIFileUploader
  fieldName="pictures"
  value={files}
  onValueChange={({ newValue }) => setFiles(newValue)}
  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.

<MUIFileUploader
  fieldName="documents"
/>

Disable drag-and-drop:

<MUIFileUploader
  fieldName="documents"
  disableDragAndDrop
/>

Custom Drop Zone#

Customize the drop zone appearance and behavior using dropZoneProps.

<MUIFileUploader
  fieldName="documents"
  dropZoneProps={({ isDragging, disabled, error }) => ({
    sx: {
      borderColor: error
        ? 'error.main'
        : isDragging
          ? 'primary.main'
          : 'grey.400',
      opacity: disabled ? 0.5 : 1
    }
  })}
/>

Where:

type FileUploaderDropZoneState = {
  /** 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.

<MUIFileUploader
  fieldName="documents"
  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.

<MUIFileUploader
  fieldName="documents"
  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.

<MUIFileUploader
  fieldName="documents"
  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.

<MUIFileUploader
  fieldName="documents"
  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;
}

Props#

Props marked with * are required.

NameTypeDescription
fieldName*string
Name/path of the field. Used to derive the id, the default label, and the name attribute. This prop is required for all components.
valueFile | File[] | null
Currently selected file(s). undefined/null are treated as no files selected.
onValueChange*({ newValue, event }) => void
Called with the accepted file value after every upload, removal, or clear action — File, File[], or null when cleared.
acceptstring
Comma-separated list of accepted file types, e.g. image/* or .pdf,.doc,.docx.
multipleboolean
When true, allows selecting multiple files.
maxSizenumber
Maximum file size (in bytes) eligible for upload. Larger files are rejected and reported through onUploadError.
maxFilesnumber
Maximum number of files that can be uploaded. Excess files are rejected and reported through onUploadError. Files in existingFiles count against the limit.
onUploadError(errors: FileUploadErrorDetails[]) => void
Callback fired when uploaded files fail type, size, or count validation.
dropZonePropsBoxProps | (state) => BoxProps
Props applied to the drag-and-drop wrapper Box. Pass an object, or a callback receiving { isDragging, disabled, error }. Ignored when disableDragAndDrop is true.
disableDragAndDropboolean
Disable drag-and-drop and only allow file selection via the upload button.
Default: false
renderUploadButton(fileInput: ReactNode) => ReactNode
Custom upload button renderer. Receives the hidden file input as children/content.
existingFilesExistingUploadedFile[]
Pre-existing server-side files, displayed separately from new uploads via renderExistingFileItem.
renderExistingFileItem({ file, index }) => ReactNode
Custom renderer for each file passed through existingFiles. These files are not part of value and are not removed automatically.
renderFileItem({ file, index, removeFile }) => ReactNode
Custom renderer for each newly selected file. Call the provided removeFile(event) from your remove button to delete the file from the value.
existingFileListPropsBoxProps
Props applied to the wrapper Box containing existing files.
uploadedFileListPropsBoxProps
Props applied to the wrapper Box containing new uploads.
inputRefRef<HTMLInputElement>
Ref for the hidden file <input> element.
labelReactNode
Label displayed for the field. Defaults to a human-readable label derived from fieldName, e.g. firstName becomes First Name
showLabelAboveFormFieldboolean
When true, renders the field label above the form field in the FormLabel component, instead of inside or beside it.
fullWidthboolean
When true, the component expands to fill its container width.
Default: false
formLabelPropsFormLabelProps
FormLabelProps forwarded to the internal FormLabel. The id is managed by the component. Multiple fields can be configured using the ConfigProvider component.
hideLabelboolean
When true, hides the rendered field label while preserving accessible labeling where possible.
requiredboolean
Indicates that the field is mandatory by adding an asterisk symbol (*) to the form label and setting the relevant accessibility attributes.
errorMessagestring | string[]
Validation error for the field — pass a single message string, or a string[] when the field can fail multiple rules at once (every message is shown together). A non-empty string or array puts the field in an error state; undefined/''/[] clear it. Normalize your form library's error shape to this at the call site (e.g. an RHF FieldError via its .message). Use renderError to customize how the message(s) are rendered.
renderError(errors: string[]) => ReactNode
Custom renderer for the resolved error message(s), called only when the field is in an error state. Always receives a string[] — use errors[0] for the common single-message case, or map over errors when a field fails several rules. By default a single message renders as text and multiple messages render on separate lines.
hideErrorMessageboolean
If true, hides the error message text while keeping the field in an error state.
helperTextReactNode
Content displayed in the FormHelperText component below the field when there is no visible validation error.
formHelperTextPropsFormHelperTextProps
FormHelperTextProps forwarded to the internal FormHelperText. The id is managed by the component. Multiple fields can be configured using the ConfigProvider component.
customIds{ field, label, helperText, error }
Overrides the default field, label, helper text, and error IDs used for accessibility.

Playground#

Driven by plain React state — a single-image uploader with accept / maxSize and an onUploadError handler, plus a multi-file uploader with maxFiles, fullWidth and drag-and-drop.