File Uploader
A file uploader is a form element used to upload multiple files.
const FileUploaderExample = () => {return (<FileUploader name="Default File Uploader" required><FileUploaderLabel>Upload files</FileUploaderLabel><FileUploaderHelpText>Files can be up to 50 MB.</FileUploaderHelpText><FileUploaderDropzone acceptedMimeTypes={['image/*', 'application/pdf']}><FileUploaderDropzoneText>Browse for files or drag them here</FileUploaderDropzoneText></FileUploaderDropzone><FileUploaderItemsList><FileUploaderItem fileIcon={<DownloadIcon decorative />} ><FileUploaderItemTitle>File1.png</FileUploaderItemTitle><FileUploaderItemDescription>9.2 MB</FileUploaderItemDescription></FileUploaderItem><FileUploaderItem fileIcon={<DownloadIcon decorative />}><FileUploaderItemTitle>File2.png</FileUploaderItemTitle><FileUploaderItemDescription>10.7 MB</FileUploaderItemDescription></FileUploaderItem><FileUploaderItem fileIcon={<DownloadIcon decorative />}><FileUploaderItemTitle>File3.png</FileUploaderItemTitle><FileUploaderItemDescription>4.1 MB</FileUploaderItemDescription></FileUploaderItem></FileUploaderItemsList></FileUploader>);}render(<FileUploaderExample />)
A File Uploader is a form element that can trigger a file browser or accept drag-and-drop attachments. It can accept multiple attachments. The package consists of an associated label (FileUploaderLabel
), help text (FileUploaderInfo
), the dropzone (FileUploaderDropzone
), and cards for each uploaded attachment (FileUploaderItem
).
- The File Uploader should always use
FileUploaderLabel
to ensure it has an associated label. - The only tab stop in the File Uploader is the visually hidden input within the dropzone. The “Enter” and “Space” keys open the file selector.
- Use an accessible option for drag and drop implementation. See the Drag and drop section below for more guidance.
File Picker is another form component that, like File Uploader, allows the user to upload an attachment. There are a few key differences between the two components. File Uploader is more dynamic than File Picker – it allows multiple attachments and all of them are removable by the user. File Picker allows the user to upload only one file at a time with no way to clear the selection if a file has already been uploaded (besides selecting a new file). Because File Uploader has the ability to display loading and real-time error states, it’s a safer bet than File Picker for complex or potentially risky user flows.
Visually, File Picker is much more compact than the File Uploader, so it will fit snugly into your form UI, and won’t stand out as much as File Uploader, which takes up more vertical and horizontal space.
The File Uploader doesn’t come with drag and drop functionality built into the dropzone. When implementing drag and drop, please use an option that provides full keyboard, screen reader, and mouse capabilities. One highly recommended option is React Aria.
There are various drag and drop technologies that don’t include accessible solutions (like ReactDnD, for example) so please take the time to ensure the accessibility of your implementation. For more information on drag and drop accessibility, read the WCAG guidelines.
See the example below for an implementation that uses plain JavaScript.
const sampleFiles = [{variant: 'default',title: 'File1.png',description: '9.2 MB',id: 'initial-file-0',},{variant: 'loading',title: 'File2.png',description: 'Uploading...',id: 'initial-file-1',},];const DragDropExample = () => {const [screenReaderText, setScreenReaderText] = React.useState('');const [files, setFiles] = React.useState(sampleFiles);React.useEffect(() => {const timer = setTimeout(() => {let finishedFiles = '';setFiles((prev) => {const updatedFiles = [];prev.forEach((file) => {if (file.variant === 'loading') {file.variant = 'default';const size = Math.floor(Math.random() * (50 - 1 + 1) + 1);file.description = size + ' ' + 'MB';finishedFiles = finishedFiles + ' ' + file.title;}updatedFiles.push(file);});return updatedFiles;});if (finishedFiles.length > 0) {setScreenReaderText('Finished uploading: ' + finishedFiles);}}, 3000);return () => clearTimeout(timer);}, [files, setFiles]);const handleInputChange = (event)=> {const {files: newFiles} = event.target;let newFilesNames = '';if (newFiles !== null) {Array.from(newFiles).forEach(({name}) => {newFilesNames = newFilesNames + ' ' + name;setFiles((prev) => {return [...prev,{title: name,description: 'Uploading...',variant: 'loading',id: snakeCase(name),},];});});}if (newFilesNames.length > 1) {setScreenReaderText('uploading: ' + newFilesNames);}};const handleDragEnter = (event) => {const {items} = event.dataTransfer;setScreenReaderText('Dragging ' + items.length + ' files');};const handleDragLeave = (event) => {const {items} = event.dataTransfer;setScreenReaderText('Cancelled dragging ' + items.length + ' files');};const handleDrop = (event) => {const {files: newFiles} = event.dataTransfer;setScreenReaderText('Dropped ' + newFiles.length + ' files');if (newFiles !== null) {Array.from(newFiles).forEach(({name}) => {setFiles((prev) => {return [...prev,{title: name,description: 'Uploading...',variant: 'loading',id: snakeCase(name),},];});});}};return (<><FileUploader name="Default File Uploader"><FileUploaderLabel>Upload files</FileUploaderLabel><FileUploaderHelpText>Files can be up to 50 MB.</FileUploaderHelpText><FileUploaderDropzonemultipleacceptedMimeTypes={['image/*', 'application/pdf']}onDragEnter={handleDragEnter}onDrop={handleDrop}onDragLeave={handleDragLeave}onInputChange={handleInputChange}><FileUploaderDropzoneText>Browse for files or drag them here</FileUploaderDropzoneText></FileUploaderDropzone><FileUploaderItemsList>{files.map(({variant, title, description, id}) => (<FileUploaderItemvariant={variant}key={id}fileIcon={<DownloadIcon decorative />}onButtonClick={() => {setFiles((prev) => {return prev.filter((file) => file.id !== id);});}}><FileUploaderItemTitle>{title}</FileUploaderItemTitle><FileUploaderItemDescription>{description}</FileUploaderItemDescription></FileUploaderItem>))}</FileUploaderItemsList></FileUploader><ScreenReaderOnly aria-live="assertive">{screenReaderText}</ScreenReaderOnly></>);}render(<DragDropExample />)
Use a default File Uploader when your user needs to upload any number of files.
const FileUploaderExample = () => {return (<FileUploader name="Default File Uploader"><FileUploaderLabel>Upload files</FileUploaderLabel><FileUploaderHelpText>Files can be up to 50 MB.</FileUploaderHelpText><FileUploaderDropzone acceptedMimeTypes={['image/*', 'application/pdf']}><FileUploaderDropzoneText>Browse for files or drag them here</FileUploaderDropzoneText></FileUploaderDropzone><FileUploaderItemsList><FileUploaderItem fileIcon={<DownloadIcon decorative />}><FileUploaderItemTitle>File1.png</FileUploaderItemTitle><FileUploaderItemDescription>9.2 MB</FileUploaderItemDescription></FileUploaderItem><FileUploaderItem variant="loading"><FileUploaderItemTitle>File2.png</FileUploaderItemTitle><FileUploaderItemDescription>Uploading...</FileUploaderItemDescription></FileUploaderItem></FileUploaderItemsList></FileUploader>);}render(<FileUploaderExample />)
const FileUploaderExample = () => {return (<FileUploader name="Default File Uploader" required><FileUploaderLabel>Upload files</FileUploaderLabel><FileUploaderHelpText>Files can be up to 50 MB.</FileUploaderHelpText><FileUploaderDropzone acceptedMimeTypes={['image/*', 'application/pdf']}><FileUploaderDropzoneText>Browse for files or drag them here</FileUploaderDropzoneText></FileUploaderDropzone><FileUploaderItemsList><FileUploaderItem fileIcon={<DownloadIcon decorative />} ><FileUploaderItemTitle>File1.png</FileUploaderItemTitle><FileUploaderItemDescription>9.2 MB</FileUploaderItemDescription></FileUploaderItem><FileUploaderItem fileIcon={<DownloadIcon decorative />}><FileUploaderItemTitle>File2.png</FileUploaderItemTitle><FileUploaderItemDescription>10.7 MB</FileUploaderItemDescription></FileUploaderItem><FileUploaderItem fileIcon={<DownloadIcon decorative />}><FileUploaderItemTitle>File3.png</FileUploaderItemTitle><FileUploaderItemDescription>4.1 MB</FileUploaderItemDescription></FileUploaderItem></FileUploaderItemsList></FileUploader>);}render(<FileUploaderExample />)
Use a disabled File Uploader to show users that they can’t interact with the component.
const FileUploaderExample = () => {return (<FileUploader name="Default File Uploader" disabled><FileUploaderLabel>Upload files</FileUploaderLabel><FileUploaderHelpText>Files can be up to 50 MB.</FileUploaderHelpText><FileUploaderDropzone acceptedMimeTypes={['image/*', 'application/pdf']}><FileUploaderDropzoneText>Browse for files or drag them here</FileUploaderDropzoneText></FileUploaderDropzone></FileUploader>);}render(<FileUploaderExample />)
Use the loading state of FileUploaderItem so the user doesn’t move on before their action is complete or while the upload might still fail.
const FileUploaderExample = () => {return (<FileUploader name="Default File Uploader"><FileUploaderLabel>Upload files</FileUploaderLabel><FileUploaderHelpText>Files can be up to 50 MB.</FileUploaderHelpText><FileUploaderDropzone acceptedMimeTypes={['image/*', 'application/pdf']}><FileUploaderDropzoneText>Browse for files or drag them here</FileUploaderDropzoneText></FileUploaderDropzone><FileUploaderItemsList><FileUploaderItem fileIcon={<DownloadIcon decorative />} ><FileUploaderItemTitle>File1.png</FileUploaderItemTitle><FileUploaderItemDescription>9.2 MB</FileUploaderItemDescription></FileUploaderItem><FileUploaderItem fileIcon={<DownloadIcon decorative />}><FileUploaderItemTitle>File2.png</FileUploaderItemTitle><FileUploaderItemDescription>10.7 MB</FileUploaderItemDescription></FileUploaderItem><FileUploaderItem variant="loading"><FileUploaderItemTitle>File3.png</FileUploaderItemTitle><FileUploaderItemDescription>Uploading...</FileUploaderItemDescription></FileUploaderItem></FileUploaderItemsList></FileUploader>);}render(<FileUploaderExample />)
If an upload fails or doesn’t meet the acceptable criteria, use the File Uploader in its error state. The user will still be able to upload files. Be sure to also display the failed FileUploaderAttachmentItem in its error state so the user knows where to direct their attention. See composition notes below for guidance around error state copy.
const FileUploaderExample = () => {return (<FileUploader name="Default File Uploader" required><FileUploaderLabel>Upload files</FileUploaderLabel><FileUploaderHelpText>Files can be up to 50 MB.</FileUploaderHelpText><FileUploaderDropzone acceptedMimeTypes={['image/*', 'application/pdf']}><FileUploaderDropzoneText>Browse for files or drag them here</FileUploaderDropzoneText></FileUploaderDropzone><FileUploaderErrorText>One of your files failed to upload. Please try again.</FileUploaderErrorText><FileUploaderItemsList><FileUploaderItem fileIcon={<DownloadIcon decorative />} ><FileUploaderItemTitle>File1.png</FileUploaderItemTitle><FileUploaderItemDescription>9.2 MB</FileUploaderItemDescription></FileUploaderItem><FileUploaderItem variant="error"><FileUploaderItemTitle>File2.png</FileUploaderItemTitle><FileUploaderItemDescription>This file is too big. You can upload files up to 50 MB.</FileUploaderItemDescription></FileUploaderItem></FileUploaderItemsList></FileUploader>);}render(<FileUploaderExample />)
Always include a Label with the File Uploader. Labels should clearly describe the file(s) being requested. They should be concise and descriptive, not instructional.
- Use File Uploader Info as help text to provide instruction if needed. For example, don't use "Upload an image in the form of a jpeg or png" as label text. Instead, use "Profile photo" as a label and "Upload an image file" as help text.
- Avoid articles ("a", "the") and punctuation. For example, use "SIM registration code" rather than "The SIM registration code".
File limitations should be communicated to the user up front to help avoid errors, like uploading an incompatible file type or one that’s too large.
Make each limitation its own sentence. Use positive framing to clearly communicate limitations to the user:
Requirements | Recommended phrasing |
---|---|
File type | You can upload [x], [x] and [x] file formats. |
File size | Files can be up to [file limit]. |
Number of files | You can upload up to [max #] files. |
Using the FileUploaderItem
with your File Uploader will allow users to receive instant feedback on progress of the file upload.
Truncate the file name in the middle if it’s too long.
Include description text that communicates when it’s uploading, when it’s successful, or when it fails (and why). If the delete action is destructive, consider using the Delete pattern for communicating the severity.
Requirements | Recommended phrasing |
---|---|
File uploading | Uploading... |
File size | Display the file size (e.g., 15 MB) |
In the case of an upload error, the card displaying the failed file should be in error state and include an error message that clearly communicates why it wasn’t uploaded. These error types may include invalid file type or file over the file size limit. We recommend the following phrasing:
Status | Recommended phrasing |
---|---|
Invalid file type | This file is not an accepted format. You can upload [x], [x] and [x] file formats. |
Exceeds file size limit | This file size is too big. You can upload files up to [file limit]. |
Network errors (internet connection dropped while uploading, request timeout, service is down) | Something went wrong with the network. Check your internet connection, then try again. |
Too many uploads in a certain amount of time | We couldn’t upload so many files so quickly. Try uploading files more slowly, or try again later. |
Generic (encountered an internal error) | Something went wrong. Try uploading your files again. |
Note: be sure to also put the File Uploader in error state and include an error message underneath the FileUploaderDropzone in the case of an upload failure. This will ensure the user notices the failed File Uploader Item, even if it’s at the bottom of a long list of attachments.
For additional guidance on how to compose error messages, refer to the error state pattern.
Do
Use a File Uploader when you need users to upload multiple files.
Don't
Use File Uploader for a single file upload if you’re tight on UI space – use File Picker instead.
Do
Always include a label with the File Uploader (use the FileUploaderLabel).
Don't
Use FileUploader without FileUploaderLabel.
Do
Be sure to communicate to your user when a file is still uploading using the loading state for the FileUploaderItem.
Don't
Let users remove uploaded files that may be destructive actions without a warning – instead use the Delete Pattern.
Do
Use descriptive help and error text to communicate the types of files that are allowed.
Don't
Display a FileUploaderItem in error state without putting the FileUploader itself into error state and providing a helpful error message.