import { FileUpload } from '@uhg-abyss/web/ui/FileUpload';Upload patterns
The FileUpload component supports two primary upload patterns:
-
Queued Uploads - Files are added to a local queue in the component. Intake rules (e.g.,
fileTypes,maxFileSize,customValidation) run immediately, but the actual upload happens later, typically after a form submit or explicit action. -
Immediate Uploads - Files begin uploading to your server as soon as they are added. Controlled via
onValidate, with full control over progress, errors, and cancellation. Does not require a submit button.
See Immediate upload for details on implementing the second pattern.
Note: The majority of examples in this documentation use the queued upload pattern.
Display properties
Label
Use the label prop to display a label above the input. To hide the input label, set hideLabel to true.
Use isRequired and isOptional for further customization.
Note: If using useForm, do not use isRequired. The same functionality can be achieved with required: true in validators.
Helper
Use the helper prop to display a help icon next to the label. Simply passing a string value will render the default helper, a Tooltip containing that string. The helper can be customized by passing in a node. It is recommended to use either a Tooltip or a Popover. See When should I use a Tooltip vs. a Popover? for more information on best practices regarding the two.
Subtext
Use the subText prop to display helpful information related to the input field, such as accepted file types or maximum file size. The prop accepts a string.
Hide upload icon
Set the hideUploadIcon prop to true to hide the upload icon in the drag-and-drop area. The default value is false.
Variant
Use the variant prop to change the visual style of the FileUpload. The possible values are 'default' and 'minimal'. The default value is 'default'.
On mobile screens, the view will automatically change for both variants to save space and improve usability.
Max height
By default, the space for the file list below the dropzone will expand to fit all files added. To restrict the height and enable scrolling for large file lists, use the maxHeight prop. The default is 100%, which allows the file list to expand to fill the available space of its container.
Custom rendering & content
File thumbnails & previews
Use the renderCustomThumbnail and renderCustomPreview props to add support of previews for custom file types beyond images. The component provides the modal wrapper and pagination controls automatically.
interface QueuedFile { file: File; status: 'default' | 'uploading' | 'error' | 'success'; uploadProgress: number; error?: FileError; hideThumbnail?: boolean; key: string;}
type renderCustomThumbnail = (file: QueuedFile) => React.ReactNode;
type renderCustomPreview = ( file: QueuedFile, pageIndex: number) => { content: React.ReactNode; pageCount?: number } | null;Additional content
Any child elements of the FileUpload will be added below the component.
File name formatting
Use the formatFileName callback to customize how file names are displayed in the file list. This is useful for implementing custom truncation with ellipsis for long file names. When this prop is provided, a tooltip with the full file name will automatically appear on hover.
The callback receives the file name and should return the formatted string to display.
Form integration
useForm (recommended)
Using useForm and FormProvider simplifies form management by providing out-of-the-box validation, form state handling, and performance optimizations, leveraging the power of react-hook-form.
useState
Using the useState hook gets values from the component state.
Validation
Validators (useForm)
Use the validators prop to set validation rules for the field when using useForm. The validators prop validates the entire file array at the form level (overall system validation). This is different from per-file validation that happens through maxFileSize, fileTypes, and customValidation props.
See the examples below for implementation on various types of validation.
Note: The validators prop receives an array of File objects, not QueuedFile objects.
Custom validators
Use the validate property within validators to create custom validation rules that operate on the entire file array. This allows you to enforce complex business rules like checking for duplicate files, validating total file size, or ensuring specific file combinations.
Error message (useState)
Use the errorMessage prop to display a custom error message in an Alert below the input field when using useState.
Error alert customization
Use the errorConfig prop to customize the error alert that appears when validation fails. You can set a custom title and heading level for accessibility.
errorConfig?: { title?: string; headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;}File intake rules
File types
Use the fileTypes prop to specify the allowed file types for the file upload. The prop accepts an object whose keys are MIME type and whose values are the corresponding file extensions for that type.
Max file size
Use the maxFileSize prop to limit the maximum allowed file size (in MB). If a file is selected that exceeds the file size limit it will be not be added to the file upload queue and an error message will be displayed. By default, there is no file size limit.
Custom file intake rule
Use the customValidation prop to provide a synchronous validation function that is called when files are dropped or selected. This function is executed immediately as part of react-dropzone's built-in validation, allowing you to reject files before they are added to the queue.
The function must be synchronous and has the following type:
(file: File) => string | null;The file parameter is a JavaScript File object. If the file is valid, return null. If invalid, return a string containing the error message to be displayed.
Capabilities:
- Executes synchronously during the file drop event
- Files that fail validation are immediately marked with
status: 'error' - Files that pass validation start with
status: 'default'
Limitations:
- Cannot set
'success'or'uploading'states - Cannot perform async operations (use Immediate upload below for async validation)
Immediate upload
For full control over file statuses, including async validation and upload operations, use the onValidate callback. This callback is triggered for each new file added to the queue, providing an updateStatus function to programmatically update the file's status, progress, and errors.
Use the onChange callback to track all file changes, and the onDelete callback to handle cleanup when successfully uploaded files are removed.
Note: The onValidate callback is only compatible with useState (uncontrolled mode). It cannot be used with useForm or controlled value/onChange patterns.
Capabilities:
- Supports async operations (API calls, file uploads, etc.)
- Full control over all file states:
'default','uploading','success','error' - Can update progress indicators and error messages
- Multiple files can be processed independently and in parallel
- Files start in
'default'state and can transition to any other state - Supports canceling uploads in progress
- Supports deleting successfully uploaded files with confirmation
interface QueuedFile { file: File; status: 'default' | 'uploading' | 'error' | 'success'; uploadProgress: number; error?: { code: string; message: string }; hideThumbnail?: boolean; key: string;}
type onValidate = ( file: QueuedFile, updateStatus: ( status: 'default' | 'uploading' | 'success' | 'error', options?: { error?: { code: string; message: string }; uploadProgress?: number; } ) => void) => void;
type onChange = (files: QueuedFile[]) => void;
type onDelete = (file: QueuedFile) => void;FileUpload Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
children | React.ReactNode | - | - | Extra content to display below the FileUpload |
className | string | - | - | CSS class name to apply to each element within the component |
css | Abyss.CSSProperties | Partial<Record<`abyss-${T}`, Abyss.CSSProperties>> | - | - | Object containing CSS styling to apply; uses the classes listed in the "Integration" tab of the component's documentation |
customValidation | (file: File) => string | null | - | - | Callback function executed each time a file is added to the queue. The function should return null if the file is valid, or a string containing the error message if the file is invalid. |
data-testid | string | - | - | Suffix used to create a unique ID used for automated testing |
disableDefaultProviderProps | boolean | false | - | If set to true, the component will not use the DefaultPropsProvider values. If you aren’t using the DefaultPropsProvider, this prop can be ignored. |
errorConfig | { title?: string | undefined; headingLevel?: 1 | 2 | 3 | 4 | 5 | 6 | undefined; } | - | - | Configuration for the error alert displayed when validation fails. |
errorMessage | string | never | - | - | Error message to display for the input. Only used when not in a FormProvider. errorMessage should not exist in useForm mode. |
fileTypes | Accept | - | - | The file types that are accepted by the FileUpload |
formatFileName | (fileName: string) => string | - | - | Callback function to customize how file names are displayed in the file list. |
helper | React.ReactNode | - | - | Helper element next to label |
hideLabel | boolean | false | - | If true, the label will be visually hidden |
hideThumbnails | boolean | false | - | If true, the file thumbnails in the file list will be hidden. |
hideUploadIcon | boolean | false | - | If true, the file upload icon will not be displayed in the drop zone. Only used when variant is 'default'. |
isOptional | boolean | false | - | If true, the label will be appended with "(Optional)" |
isRequired | boolean | never | - | - | Whether the input is required. Only used when not in a FormProvider. isRequired should not exist in useForm mode. |
label | string | - | The label for the input | |
maxFileSize | number | - | - | The maximum file size (in MB) allowed for FileUpload |
maxHeight | string | '100%' | - | The maximum height of the FileUpload component. When content exceeds this height, the file list will scroll. |
model | never | string | - | - | model should not exist in useState mode. Model name for form validation. Only used when in a FormProvider. |
onBlur | React.FocusEventHandler<HTMLInputElement> | - | - | Callback function executed when the input is blurred |
onChange | (files: QueuedFile[]) => void | - | - | Callback function executed when the input value changes. Receives an array of queued files with status information. |
onDelete | (file: QueuedFile) => void | - | - | Callback function executed when a successfully uploaded file is deleted by the user. Use this to perform cleanup operations like removing the file from your server. |
onFocus | React.FocusEventHandler<HTMLInputElement> | - | - | Callback function executed when the input is focused |
onValidate | (file: QueuedFile, updateStatus: (status: FileStatus, options?: { ...; } | undefined) => void) => void | - | - | Callback function executed when a file is added to the queue and ready for async validation/upload. Use this to perform async operations like API validation or file uploads. Call the updateStatus function to update the file's status, progress, and errors. |
renderCustomPreview | (file: QueuedFile, pageIndex: number) => { content: React.ReactNode; pageCount?: number | undefined; } | null | - | - | Callback function to provide custom preview content for file types. The modal wrapper and pagination controls are provided automatically. |
renderCustomThumbnail | (file: QueuedFile) => React.ReactNode | - | - | Callback function to provide custom thumbnail rendering for file types. |
subText | string | - | - | Additional descriptive text for the input |
successMessage | string | - | - | Success message to display for the input |
validators | never | RegisterOptions | - | - | validators should not exist in useState mode. Validators for the input. Only used when in a FormProvider. |
value | ValueType | never | - | - | The value of the input. Only used when not in a FormProvider. value should not exist in useForm mode. |
variant | "default" | "minimal" | 'default' | - | The visual variant of the FileUpload |
FileUpload Classes
| Class Name | Description |
|---|---|
| .abyss-file-upload-root | The root element of the FileUpload component |
| .abyss-file-upload-wrapper | The wrapper element containing the main FileUpload content |
| .abyss-file-upload-label-wrapper | The wrapper element for the label and subtext |
| .abyss-file-upload-label | The label element for the FileUpload |
| .abyss-file-upload-subtext | The subtext element providing additional context |
| .abyss-file-upload-drop-zone | The drag-and-drop zone element |
| .abyss-file-upload-drop-zone-content | The content within the drop zone |
| .abyss-file-upload-file-browser-button | The button to open the file browser |
| .abyss-file-upload-error-alert | The alert element displaying validation errors |
| .abyss-file-upload-file-list | The file list component element |
| .abyss-file-upload-file-list-root | The root element of the file list |
| .abyss-file-upload-file-list-item | A file list item element |
| .abyss-file-input-file-list-collapse-button | The button to collapse/expand the file list |
| .abyss-file-input-file-list-collapse-panel | The collapsible panel containing the file list |
| .abyss-file-input-file-list-list | The list element containing file items |
| .abyss-file-input-file-list-item-root | The root element of a file list item |
| .abyss-file-input-file-list-item-container | The container element of a file list item |
| .abyss-file-input-file-list-item-start-content | The start content section of a file list item |
| .abyss-file-input-file-list-item-image-thumbnail | The image thumbnail element |
| .abyss-file-input-file-list-item-indicator-root | The root element of the status indicator |
| .abyss-file-input-file-list-item-indicator-container | The container element of the status indicator |
| .abyss-file-upload-file-list-item-indicator-icon | The icon element within the status indicator |
| .abyss-file-upload-file-list-item-file-name-text | The text element displaying the file name |
| .abyss-file-upload-file-list-item-file-preview-button | The button to preview the file |
| .abyss-file-input-file-list-item-end-content | The end content section of a file list item |
| .abyss-file-upload-file-list-item-remove-button | The button to remove a file from the list |
| .abyss-file-upload-file-list-item-remove-icon | The icon within the remove button |
| .abyss-file-upload-file-list-item-upload-progress-text | The text element displaying upload progress |
| .abyss-file-upload-file-list-item-cancel-button | The button to cancel an uploading file |
| .abyss-file-upload-file-list-item-progress-bar | The progress bar showing upload progress |
| .abyss-file-upload-file-list-item-error-text | The text element displaying error messages |
| .abyss-file-upload-file-preview-image | The image element in the preview modal |
| .abyss-file-upload-file-preview-pagination | The pagination controls in the preview modal |
| .abyss-file-upload-file-preview-modal-dialog | The modal dialog for file preview |
| .abyss-file-upload-cancel-confirmation-modal-footer | The footer of the cancel confirmation modal |
| .abyss-file-upload-cancel-confirmation-modal-dialog | The modal dialog for canceling file upload |
| .abyss-file-upload-delete-confirmation-modal-footer | The footer of the delete confirmation modal |
| .abyss-file-upload-delete-confirmation-modal-dialog | The modal dialog for deleting an uploaded file |
Known issue: Reannouncement of FileUpload group and information
File selection dialog triggers full change of context
The use of the native "Select file" dialog to pick files triggers a "change of context" with the browser itself. This causes screen readers treat the return of focus to browser window as if the user changed applications. This causes screen readers to announce the full page context, starting with the page title down to the location of the "Browse Files" button.
In the case of the FileUpload component, this includes label, group role, required indication, subText, error messages and any other information using aria-describedby.
Example originally using JAWS & Chrome
FileUpload | Abyss - Google Chrome UnavailableFileUpload | Abyss - Google Chrome pageFileUpload | AbyssMainRegionTabPanelUpload graphics* group required JPEG and PNG files only; Max file name length: 20 characters, using only letters, numbers, underscores, and hyphens with no spaces; Max. file size: 5MB; Max. files: 3Browse Files ButtonNote: In the content above, all announcements prior to "Upload graphics*" are separate from the FileUpload component and cannot be addressed.
Partial solution implementation
To minimize what FileUpload announces in these cases, the component removes aria-describedby from the <fieldset> before opening the file selection dialog and restores it after focus returns to the Browse Files button that triggered it. This suppresses the announcement of subText and error messages in many cases.
Example now using JAWS & Chrome
FileUpload | Abyss - Google Chrome UnavailableFileUpload | Abyss - Google Chrome pageFileUpload | AbyssMainRegionTabPanelUpload graphics* group requiredBrowse Files ButtonInconsistent BrAT support
Unfortunately not all browser and screen reader combinations result in this reduction. The following is a sampling of the BrAT (browser AT) combinations
Works as shown above (second example)
- Windows
- JAWS & Chrome
- NVDA & Chrome
- MacOS
- VoiceOver & Chrome
Full announcement
- MacOS
- VoiceOver & Safari
Component Tokens
Note: Click on the token row to copy the token to your clipboard.
FileUpload Tokens
| Token Name | Value | |
|---|---|---|
| file-upload.color.border.drop-zone | #002677 | |
| file-upload.color.border.file-list-item.default | #CBCCCD | |
| file-upload.color.border.file-list-item.error | #990000 | |
| file-upload.color.border.file-list-item.in-progress | #CBCCCD | |
| file-upload.color.border.file-list-item.success | #007000 | |
| file-upload.color.border.thumbnail | #CBCCCD | |
| file-upload.color.icon.illustrative | #002677 | |
| file-upload.color.icon.indicator.error | #990000 | |
| file-upload.color.icon.indicator.success | #007000 | |
| file-upload.color.icon.utility.active | #4B4D4F | |
| file-upload.color.icon.utility.hover | #323334 | |
| file-upload.color.icon.utility.rest | #4B4D4F | |
| file-upload.color.surface.drop-zone.rest | #FFFFFF | |
| file-upload.color.surface.drop-zone.hover | #F3F3F3 | |
| file-upload.color.surface.file-list-item.default | #FFFFFF | |
| file-upload.color.surface.file-list-item.error | #FFFFFF | |
| file-upload.color.surface.file-list-item.in-progress | #EEF4FF | |
| file-upload.color.surface.file-list-item.success | #FFFFFF | |
| file-upload.color.surface.indicator | #FFFFFF | |
| file-upload.color.surface.progress-bar | #002677 | |
| file-upload.color.text.file-list-item.error | #990000 | |
| file-upload.border-radius.all.drop-zone | 8px | |
| file-upload.border-radius.all.file-list-item | 4px | |
| file-upload.border-radius.all.indicator | 500px | |
| file-upload.border-radius.all.thumbnail | 4px | |
| file-upload.border-width.all.drop-zone | 2px | |
| file-upload.border-width.all.file-list-item | 1px | |
| file-upload.border-width.all.thumbnail | 1px | |
| file-upload.sizing.all.icon.illustrative | 48px | |
| file-upload.sizing.all.icon.indicator | 20px | |
| file-upload.sizing.all.icon.utility | 24px | |
| file-upload.spacing.gap.horizontal.drop-zone.minimal | 8px | |
| file-upload.spacing.gap.horizontal.confirmation-modal-footer | 16px | |
| file-upload.spacing.gap.horizontal.file-list-item | 8px | |
| file-upload.spacing.gap.vertical.container | 16px | |
| file-upload.spacing.gap.vertical.drop-zone.default | 8px | |
| file-upload.spacing.gap.vertical.file-list | 12px | |
| file-upload.spacing.gap.vertical.wrapper | 8px | |
| file-upload.spacing.padding.all.cancel-button | 4px | |
| file-upload.spacing.padding.all.drop-zone.default | 24px | |
| file-upload.spacing.padding.all.file-list-item | 8px | |
| file-upload.spacing.padding.horizontal.drop-zone.minimal | 16px | |
| file-upload.spacing.padding.right.file-list | 8px | |
| file-upload.spacing.padding.vertical.drop-zone.minimal | 24px |
Header Tokens
| Token Name | Value | |
|---|---|---|
| input-header.color.text.label | #4B4D4F | |
| input-header.color.text.hint | #4B4D4F | |
| input-header.color.icon.info.rest | #196ECF | |
| input-header.color.icon.info.hover | #004BA0 | |
| input-header.color.icon.info.active | #002677 | |
| input-header.sizing.all.icon | 24px | |
| input-header.spacing.gap.horizontal.container | 4px | |
| input-header.spacing.gap.horizontal.label | 4px | |
| input-header.spacing.gap.vertical.content | 4px | |
| input-header.spacing.padding.top.content | 2px |