PatternFly

Multiple file upload

A multiple file upload component allows users to select 1 or more files to upload to a specific location. The component can be configured to support any file type as well as restrict the user's ability to upload 1 or more files. Users have the ability to select files from their system either natively using a file input or via drag and drop. Once uploaded, the user can edit any files.

Multiple file upload is able to:

  • Accept one or more files via browse or drag-and-drop
  • Provide their data to you using file objects via the onFileDrop callback prop
  • Read files as dataURLs, calling provided callbacks as needed when files start/finish being read, as well as when the read succeeds or fails
  • Display (in real time) the upload progress/status of each file
    • Uploaded files are represented by the multiple file upload status item component, this component includes the aforementioned built-in file reading logic
    • If you prefer to supply your own file reading logic and strictly use multiple file upload status item as a display component, the customFileHandler, fileName, fileSize, progressValue, and progressVariant props exist to allow you to do that

Restricting file size and type

As with singular file upload, any props accepted by react-dropzone's Dropzone component can be passed as a dropzoneProps object in order to customize the behavior of the Dropzone, such as restricting the type of files allowed. The following examples will only accept the files they list as accepted. Note that file type determination is not reliable across platforms (see the note on react-dropzone's docs about the accept prop), so be sure to test the behavior of your file upload restriction on all browsers and operating systems targeted by your application.

IMPORTANT: A note about security

Restricting file sizes and types in this way is for user convenience only, and it cannot prevent a malicious user from submitting anything to your server. As with any user input, your application should also validate, sanitize and/or reject restricted files on the server side.

Composable structure

File upload - multiple is designed in a composable manner to make customization easier. The standard sub-component relationships are arranged as follows:

<MultipleFileUpload>
  <MultipleFileUploadMain />
  <MultipleFileUploadStatus>
    <MultipleFileUploadStatusItem />
  </MultipleFileUploadStatus>
</MultipleFileUpload>

Examples

Basic

The below example demonstrates a typical application of file upload - multiple, with a few tweaks from that typical application to enhance the convenience of the example.

The "Show as horizontal" checkbox can be used to easily toggle the isHorizontal prop, showing our available styling variations.

The "Demonstrate error reporting by forcing uploads to fail" checkbox shows how our progressHelperText prop can be used to provide status messages to users, such as when a file fails to upload. While this checkbox is checked it will cause any file uploaded to automatically fail the file reading process, and helper text will be dynamically rendered which informs the user of that error.

Types

Multiple file upload uses the DropzoneOptions type from react-dropzone. It is comprised of additional props with their own types. For more information on using DropzoneOptions visit react-dropzone props and methods.

Additionally, it calls the onFileDrop callback with an event of type DropEvent. DropEvent is a union comprised of the following types:

React.DragEvent<HTMLElement>
 | React.ChangeEvent<HTMLInputElement>
 | DragEvent
 | Event

Props

MultipleFileUpload

Acts as a container for all other MultipleFileUpload sub-components. This sub-component also provides the functionality for file uploads, and access to the uploaded files via a callback.
*required
NameTypeDefaultDescription
childrenReact.ReactNodeContent rendered inside the multi upload field
classNamestringClass to add to outer div
dropzonePropsDropzoneOptions{}Optional extra props to customize react-dropzone.
isHorizontalbooleanFlag setting the component to horizontal styling mode
onFileDrop(event: DropEvent, data: File[]) => void() => {}When files are dropped or uploaded this callback will be called with all accepted files

MultipleFileUploadMain

Creates the visual upload interface, including the area to drag and drop files, an optional upload button, and descriptive instructions.
*required
NameTypeDefaultDescription
browseButtonTextstring'Upload'Visible text label for the upload button
classNamestringClass to add to outer div
infoTextReact.ReactNodeContent rendered inside the info div
isUploadButtonHiddenbooleanFlag to prevent the upload button from being rendered
titleIconReact.ReactNodeContent rendered inside the title icon div
titleTextReact.ReactNodeContent rendered inside the title text div
titleTextSeparatorReact.ReactNodeContent rendered inside the title text separator div

MultipleFileUploadStatus

Acts as an expandable container for all uploaded file statuses. An optional text and/or icon can also be passed into this sub-component. This sub-component can be conditionally rendered when at least 1 file has been attempted to be uploaded.
*required
NameTypeDefaultDescription
aria-labelstringAdds an accessible label to the list of status items.
childrenReact.ReactNodeContent rendered inside multi file upload status list
classNamestringClass to add to outer div
statusToggleIcon'danger' | 'success' | 'inProgress' | React.ReactNodeIcon to show in the status toggle
statusToggleTextstringString to show in the status toggle

MultipleFileUploadStatusItem

Automatically reads an uploaded file to render a visual representation of it, including its name, size, and read status. This sub-component also allows custom reading of files via various callbacks which will override the automatic reading behavior.
*required
NameTypeDefaultDescription
buttonAriaLabelstring'Remove from list'Adds accessibility text to the status item deletion button
classNamestringClass to add to outer div
customFileHandler(file: File) => voidA callback to process file reading in a custom way
fileFileThe file object being represented by the status item
fileIconReact.ReactNodeA custom icon to show in place of the generic file icon
fileNamestringA custom name to display for the file rather than using built in functionality to auto-fill it
fileSizenumberA custom file size to display for the file rather than using built in functionality to auto-fill it
onClearClickReact.MouseEventHandler<HTMLButtonElement>() => {}Clear button was clicked
onReadFail(error: DOMException, onReadFail: File) => void() => {}A callback for when the FileReader API fails
onReadFinished(fileHandle: File) => void() => {}A callback for when a selected file finishes loading
onReadStarted(fileHandle: File) => void() => {}A callback for when a selected file starts loading
onReadSuccess(data: string, file: File) => void() => {}A callback for when the FileReader successfully reads the file
progressAriaLabelstringAdds accessible text to the progress bar. Required when title not used and there is not any label associated with the progress bar
progressAriaLabelledBystringAssociates the progress bar with it's label for accessibility purposes. Required when title not used
progressAriaLiveMessagestring | ((loadPercentage: number) => string)Modifies the text announced by assistive technologies when the progress bar updates.
progressHelperTextReact.ReactNodeAdditional content related to the status item.
progressIdstringUnique identifier for progress. Generated if not specified.
progressValuenumberA custom value to display for the progress component rather than using built in functionality to auto-fill it
progressVariant'danger' | 'success' | 'warning'A custom variant to apply to the progress component rather than using built in functionality to auto-fill it

CSS variables

Expand or collapse columnSelectorVariableValue
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--GridTemplateColumns
1fr
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--Gap
1.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--TextAlign
center
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--GridTemplateColumns
auto
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--GridTemplateRows
auto
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--GridTemplateAreas
"title" "upload" "info"
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--Gap
1rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--PaddingTop
1.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--PaddingRight
1.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--PaddingBottom
2rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--PaddingLeft
1.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--BorderWidth
1px
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--BorderStyle
dashed
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--BorderColor
#d2d2d2
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__main--BackgroundColor
#fff
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__title--Display
grid
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__title--GridTemplateColumns
auto
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__title--Gap
0.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__title-icon--Color
#151515
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__title-text-separator--Display
block
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__title-text-separator--MarginTop
0.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__info--FontSize
0.875rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__info--Color
#6a6e73
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__info--MarginTop
0.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-drag-over__main--BorderStyle
solid
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-drag-over__main--BorderColor
#06c
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-drag-over__main--BackgroundColor
#e7f1fa
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-drag-over__main__title-icon--Color
#06c
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-drag-over__main__title-text--Color
#06c
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-drag-over__main__info--Color
#06c
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal--GridTemplateColumns
fit-content(100%)
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__main--TextAlign
start
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__main--GridTemplateColumns
1fr auto
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__main--GridTemplateAreas
"title upload" "info upload"
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__main--Gap
0.5rem 3rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__main--PaddingBottom
1.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__title--GridTemplateColumns
auto 1fr
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__title--Gap
0.25rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__title-text-separator--Display
inline
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__title-text-separator--MarginTop
0
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload--m-horizontal__info--MarginTop
0
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-progress--GridTemplateColumns
auto 1fr
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-progress--Gap
0.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-progress-icon--Color
#151515
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item--PaddingTop
1rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item--PaddingBottom
1rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item--first-child--PaddingTop
0
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item--GridTemplateColumns
auto 1fr auto
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item--Gap
0.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item--BorderWidth
1px
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item--BorderColor
#d2d2d2
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item-icon--Color
#151515
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item-close--MarginTop
calc(0.375rem * -1)
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item-progress--GridTemplateColumns
fit-content(100%) max-content
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item-progress--Gap
0.5rem
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item-progress-text--Color
#06c
.pf-v5-c-multiple-file-upload--pf-v5-c-multiple-file-upload__status-item-progress-size--Color
#6a6e73
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload--GridTemplateColumns
fit-content(100%)
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__main--TextAlign
start
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__main--GridTemplateColumns
1fr auto
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__main--GridTemplateAreas
"title upload" "info upload"
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__main--Gap
0.5rem 3rem
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__main--PaddingBottom
1.5rem
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__title--GridTemplateColumns
auto 1fr
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__title--Gap
0.25rem
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__title-text-separator--Display
inline
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__title-text-separator--MarginTop
0
.pf-v5-c-multiple-file-upload.pf-m-horizontal--pf-v5-c-multiple-file-upload__info--MarginTop
0
.pf-v5-c-multiple-file-upload.pf-m-drag-over--pf-v5-c-multiple-file-upload__main--BorderStyle
solid
.pf-v5-c-multiple-file-upload.pf-m-drag-over--pf-v5-c-multiple-file-upload__main--BorderColor
#06c
.pf-v5-c-multiple-file-upload.pf-m-drag-over--pf-v5-c-multiple-file-upload__main--BackgroundColor
#e7f1fa
.pf-v5-c-multiple-file-upload.pf-m-drag-over--pf-v5-c-multiple-file-upload__title-icon--Color
#06c
.pf-v5-c-multiple-file-upload.pf-m-drag-over--pf-v5-c-multiple-file-upload__title-text--Color
#06c
.pf-v5-c-multiple-file-upload.pf-m-drag-over--pf-v5-c-multiple-file-upload__info--Color
#06c
.pf-v5-c-multiple-file-upload__status-item:first-child--pf-v5-c-multiple-file-upload__status-item--PaddingTop
0

View source on GitHub