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.
| Name | Type | Description |
|---|---|---|
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. |
value | File | 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. |
accept | string | Comma-separated list of accepted file types, e.g. image/* or .pdf,.doc,.docx. |
multiple | boolean | When true, allows selecting multiple files. |
maxSize | number | Maximum file size (in bytes) eligible for upload. Larger files are rejected and reported through onUploadError. |
maxFiles | number | 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. |
dropZoneProps | BoxProps | (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. |
disableDragAndDrop | boolean | 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. |
existingFiles | ExistingUploadedFile[] | 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. |
existingFileListProps | BoxProps | Props applied to the wrapper Box containing existing files. |
uploadedFileListProps | BoxProps | Props applied to the wrapper Box containing new uploads. |
inputRef | Ref<HTMLInputElement> | Ref for the hidden file <input> element. |
label | ReactNode | Label displayed for the field. Defaults to a human-readable label derived from fieldName, e.g. firstName becomes First Name |
showLabelAboveFormField | boolean | When true, renders the field label above the form field in the FormLabel component, instead of inside or beside it. |
fullWidth | boolean | When true, the component expands to fill its container width. Default: false |
formLabelProps | FormLabelProps | FormLabelProps forwarded to the internal FormLabel. The id is managed by the component. Multiple fields can be configured using the ConfigProvider component. |
hideLabel | boolean | When true, hides the rendered field label while preserving accessible labeling where possible. |
required | boolean | Indicates that the field is mandatory by adding an asterisk symbol (*) to the form label and setting the relevant accessibility attributes. |
errorMessage | string | 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. |
hideErrorMessage | boolean | If true, hides the error message text while keeping the field in an error state. |
helperText | ReactNode | Content displayed in the FormHelperText component below the field when there is no visible validation error. |
formHelperTextProps | FormHelperTextProps | 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.