Skip to main content

FileUpload

An HTML5 file upload component with a drag-drop zone and file browser for selection.

Submit feedback
github

Migration Information

For teams migrating from the V1 to V2 component, please refer to the migration guide for changes to the component.
Component Guide
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

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)

Form Compatibility
useState
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)

Form Compatibility
useState
useForm

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

Form Compatibility
useState
useForm

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

NameTypeDefaultRequiredDescription
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

See CSS Prop Styling for more information
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

See Component Testing for more information
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 NameDescription
.abyss-file-upload-rootThe root element of the FileUpload component
.abyss-file-upload-wrapperThe wrapper element containing the main FileUpload content
.abyss-file-upload-label-wrapperThe wrapper element for the label and subtext
.abyss-file-upload-labelThe label element for the FileUpload
.abyss-file-upload-subtextThe subtext element providing additional context
.abyss-file-upload-drop-zoneThe drag-and-drop zone element
.abyss-file-upload-drop-zone-contentThe content within the drop zone
.abyss-file-upload-file-browser-buttonThe button to open the file browser
.abyss-file-upload-error-alertThe alert element displaying validation errors
.abyss-file-upload-file-listThe file list component element
.abyss-file-upload-file-list-rootThe root element of the file list
.abyss-file-upload-file-list-itemA file list item element
.abyss-file-input-file-list-collapse-buttonThe button to collapse/expand the file list
.abyss-file-input-file-list-collapse-panelThe collapsible panel containing the file list
.abyss-file-input-file-list-listThe list element containing file items
.abyss-file-input-file-list-item-rootThe root element of a file list item
.abyss-file-input-file-list-item-containerThe container element of a file list item
.abyss-file-input-file-list-item-start-contentThe start content section of a file list item
.abyss-file-input-file-list-item-image-thumbnailThe image thumbnail element
.abyss-file-input-file-list-item-indicator-rootThe root element of the status indicator
.abyss-file-input-file-list-item-indicator-containerThe container element of the status indicator
.abyss-file-upload-file-list-item-indicator-iconThe icon element within the status indicator
.abyss-file-upload-file-list-item-file-name-textThe text element displaying the file name
.abyss-file-upload-file-list-item-file-preview-buttonThe button to preview the file
.abyss-file-input-file-list-item-end-contentThe end content section of a file list item
.abyss-file-upload-file-list-item-remove-buttonThe button to remove a file from the list
.abyss-file-upload-file-list-item-remove-iconThe icon within the remove button
.abyss-file-upload-file-list-item-upload-progress-textThe text element displaying upload progress
.abyss-file-upload-file-list-item-cancel-buttonThe button to cancel an uploading file
.abyss-file-upload-file-list-item-progress-barThe progress bar showing upload progress
.abyss-file-upload-file-list-item-error-textThe text element displaying error messages
.abyss-file-upload-file-preview-imageThe image element in the preview modal
.abyss-file-upload-file-preview-paginationThe pagination controls in the preview modal
.abyss-file-upload-file-preview-modal-dialogThe modal dialog for file preview
.abyss-file-upload-cancel-confirmation-modal-footerThe footer of the cancel confirmation modal
.abyss-file-upload-cancel-confirmation-modal-dialogThe modal dialog for canceling file upload
.abyss-file-upload-delete-confirmation-modal-footerThe footer of the delete confirmation modal
.abyss-file-upload-delete-confirmation-modal-dialogThe 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 Unavailable
FileUpload | Abyss - Google Chrome page
FileUpload | Abyss
MainRegion
TabPanel
Upload 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: 3
Browse Files Button

Note: 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 Unavailable
FileUpload | Abyss - Google Chrome page
FileUpload | Abyss
MainRegion
TabPanel
Upload graphics* group required
Browse Files Button

Inconsistent 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 NameValue
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 NameValue
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
Table of Contents