The File Input component renders a single-file form field with QDS input styling and file validation support.
import {FileInput} from "@qualcomm-ui/react/file-input"Overview
The File Input component supports both click-to-browse and drag-and-drop interactions for a single file input. An advanced file upload component is currently in development and will support multi-file lists, async uploads, previews, and rejected-file workflows.
Examples
Simple
The simple <FileInput> bundles all subcomponents together into a single component.
<FileInput
className="w-full max-w-sm"
label="Upload file"
placeholder="Select a file"
startIcon={Upload}
/>
Composite
Use the composite API for explicit control over layout or part props.
<FileInput.Root
accept={[".pdf"]}
className="w-full max-w-sm"
startIcon={Upload}
>
<FileInput.Label>Upload agreement</FileInput.Label>
<FileInput.Control>
<FileInput.Display placeholder="Select a PDF" />
</FileInput.Control>
<FileInput.HiddenInput />
</FileInput.Root>
Sizes
The File Input component supports three size variants: sm, md (default), and lg.
<div className="flex w-full flex-col items-center gap-6">
<FileInput
className="w-full max-w-sm"
label="Upload file (Small)"
placeholder="Select a file"
size="sm"
startIcon={Upload}
/>
<FileInput
className="w-full max-w-sm"
label="Upload file (Medium)"
placeholder="Select a file"
size="md"
startIcon={Upload}
/>
<FileInput
className="w-full max-w-sm"
label="Upload file (Large)"
placeholder="Select a file"
size="lg"
startIcon={Upload}
/>
</div>
Errors
Set invalid and errorText for field-level validation messages. File validation is configured with props such as accept, maxFileSize, and validate.
<FileInput
accept={[".pdf"]}
className="w-full max-w-sm"
errorText="Upload a PDF file under 5 MB"
invalid
label="Tax document"
maxFileSize={5 * 1024 * 1024}
placeholder="Select a PDF"
required
startIcon={Upload}
/>
Disabled
Set disabled on the root or simple component.
<div className="flex w-full max-w-sm flex-col gap-4">
<Checkbox
checked={agreed}
label="I agree to the terms and conditions"
onCheckedChange={setAgreed}
/>
<FileInput
disabled={!agreed}
label="Upload approval"
placeholder="Select a file"
startIcon={Upload}
/>
</div>
API
<FileInput />
The FileInput extends FileInput.Root with the following props:
neverbooleantrue, renders a clear button that resets the selected file on click.
The button only appears when a file has been selected.{
render?:
| Element
| ((
props: Props,
) => Element)
}
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
{
placeholder?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
{
children?: ReactNode
icon?:
| LucideIcon
| ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
{
render?:
| Element
| ((
props: Props,
) => Element)
}
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
Composite API
<FileInput.Root>
| Record<string, string[]>
| 'image/png'
| 'image/gif'
| 'image/jpeg'
| 'image/svg+xml'
| 'image/webp'
| 'image/avif'
| 'image/heic'
| 'image/bmp'
| 'application/pdf'
| 'application/zip'
| 'application/json'
| 'application/xml'
| 'application/msword'
| 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
| 'application/vnd.ms-excel'
| 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
| 'application/vnd.ms-powerpoint'
| 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
| 'application/rtf'
| 'application/x-rar'
| 'application/x-7z-compressed'
| 'application/x-tar'
| 'application/vnd.microsoft.portable-executable'
| 'text/css'
| 'text/csv'
| 'text/html'
| 'text/markdown'
| 'text/plain'
| 'font/ttf'
| 'font/otf'
| 'font/woff'
| 'font/woff2'
| 'font/eot'
| 'font/svg'
| 'video/mp4'
| 'video/webm'
| 'video/ogg'
| 'video/quicktime'
| 'video/x-msvideo'
| 'audio/mpeg'
| 'audio/ogg'
| 'audio/wav'
| 'audio/webm'
| 'audio/aac'
| 'audio/flac'
| 'audio/x-m4a'
| 'image/*'
| 'audio/*'
| 'video/*'
| 'text/*'
| 'application/*'
| 'font/*'
| AnyString
| Array<
| 'image/png'
| 'image/gif'
| 'image/jpeg'
| 'image/svg+xml'
| 'image/webp'
| 'image/avif'
| 'image/heic'
| 'image/bmp'
| 'application/pdf'
| 'application/zip'
| 'application/json'
| 'application/xml'
| 'application/msword'
| 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
| 'application/vnd.ms-excel'
| 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
| 'application/vnd.ms-powerpoint'
| 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
| 'application/rtf'
| 'application/x-rar'
| 'application/x-7z-compressed'
| 'application/x-tar'
| 'application/vnd.microsoft.portable-executable'
| 'text/css'
| 'text/csv'
| 'text/html'
| 'text/markdown'
| 'text/plain'
| 'font/ttf'
| 'font/otf'
| 'font/woff'
| 'font/woff2'
| 'font/eot'
| 'font/svg'
| 'video/mp4'
| 'video/webm'
| 'video/ogg'
| 'video/quicktime'
| 'video/x-msvideo'
| 'audio/mpeg'
| 'audio/ogg'
| 'audio/wav'
| 'audio/webm'
| 'audio/aac'
| 'audio/flac'
| 'audio/x-m4a'
| 'image/*'
| 'audio/*'
| 'video/*'
| 'text/*'
| 'application/*'
| 'font/*'
| AnyString
>
['image/*', '.pdf']). Supports MIME types,
file extensions, or custom validation.Array<File>
onFileChange for controlled state.boolean| 'user'
| 'environment'
Array<File>
'ltr' | 'rtl'
booleanboolean() =>
| Node
| ShadowRoot
| Document
Partial<{
dropzone: string
errorText: string
hiddenInput: string
item: string[]
itemName: string[]
itemPreview: string[]
itemSizeText: string[]
label: string
root: string
trigger: string
}>
booleanstringnumbernumbernumberstring(details: {
files: Array<File>
}) => void
(details: {
acceptedFiles: Array<File>
rejectedFiles: Array<{
errors: Array<
| 'TOO_MANY_FILES'
| 'FILE_INVALID_TYPE'
| 'FILE_TOO_LARGE'
| 'FILE_TOO_SMALL'
| 'FILE_INVALID'
| 'FILE_EXISTS'
| AnyString
>
file: File
}>
}) => void
(details: {
files: Array<{
errors: Array<
| 'TOO_MANY_FILES'
| 'FILE_INVALID_TYPE'
| 'FILE_TOO_LARGE'
| 'FILE_TOO_SMALL'
| 'FILE_INVALID'
| 'FILE_EXISTS'
| AnyString
>
file: File
}>
}) => void
boolean| ReactElement
| ((
props: object,
) => ReactElement)
boolean| 'sm'
| 'md'
| 'lg'
| LucideIcon
| ReactNode
(
files: Array<File>,
) => Promise<Array<File>>
{
listLabel?: string
}
(
file: File,
details: {
acceptedFiles: Array<File>
rejectedFiles: Array<{
errors: Array<
| 'TOO_MANY_FILES'
| 'FILE_INVALID_TYPE'
| 'FILE_TOO_LARGE'
| 'FILE_TOO_SMALL'
| 'FILE_INVALID'
| 'FILE_EXISTS'
| AnyString
>
file: File
}>
},
) => Array<
| 'TOO_MANY_FILES'
| 'FILE_INVALID_TYPE'
| 'FILE_TOO_LARGE'
| 'FILE_TOO_SMALL'
| 'FILE_INVALID'
| 'FILE_EXISTS'
| AnyString
>
className'qui-input__root'data-disableddata-draggingdata-file-upload-part'root'data-size| 'sm'
| 'md'
| 'lg'
<FileInput.Label>
| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-input__label'data-disableddata-file-upload-part'label'data-requireddata-size| 'sm'
| 'md'
| 'lg'
<FileInput.Control>
| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-input__input-group'data-disableddata-draggingdata-file-upload-part'dropzone'data-focusdata-has-filedata-invaliddata-size| 'sm'
| 'md'
| 'lg'
tabIndex0<FileInput.ClearTrigger>
| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-input__clear-trigger'data-file-upload-part'clear-trigger'data-invaliddata-size| 'sm'
| 'md'
| 'lg'
hiddenboolean<FileInput.Display>
| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-input__input'data-size| 'sm'
| 'md'
| 'lg'
<FileInput.HiddenInput>
| ReactElement
| ((
props: object,
) => ReactElement)
<FileInput.ErrorText>
| LucideIcon
| ReactNode
| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-input__error-text'data-file-upload-part'error-text'hiddenboolean