# File Upload
URL: https://ark-ui.com/docs/components/file-upload
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/file-upload.mdx
A component that is used to upload multiple files.
---
## Anatomy
```tsx
```
## Examples
**Example: basic**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import { Paperclip, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
export component Basic() {
{'File Upload'}
{' Choose file(s)'}
component children({ context }) {
for (const file of @context.acceptedFiles; key file.name) {
}
}
}
```
### Initial Files
Use the `defaultAcceptedFiles` prop to set the initial files in the file upload component.
**Example: initial-files**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import { File as FileIcon, Paperclip, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
export component InitialFiles() {
{'File Upload'}
{' Choose file(s)'}
component children({ context }) {
for (const file of @context.acceptedFiles; key file.name) {
}
}
}
```
### Clear Trigger
Use the `ClearTrigger` to remove all uploaded files at once.
**Example: clear-trigger**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import { Paperclip, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
export component ClearTrigger() {
{'File Upload'}
{' Choose file(s)'}
{'Clear Files'}
component children({ context }) {
for (const file of @context.acceptedFiles; key file.name) {
}
}
}
```
### Dropzone
Use the `Dropzone` to enable drag-and-drop. It exposes a `data-dragging` attribute for styling.
**Example: dropzone**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import { File, Upload, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
export component Dropzone() {
{'File Upload'}
{'Drag and drop files here'}{'or click to browse'}
component children({ context }) {
for (const file of @context.acceptedFiles; key file.name) {
}
}
}
```
### Directory Upload
Use the `directory` prop to upload entire folders. Access file paths through `file.webkitRelativePath`.
**Example: directory-upload**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import { File, Folder, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
export component DirectoryUpload() {
{'Upload Folder'}
{' Select Folder'}
component children({ context }) {
for (const file of @context.acceptedFiles; key file.name) {
{file.webkitRelativePath || file.name}
}
}
}
```
> When uploading directories with many files, set `maxFiles` to a higher value or remove it entirely to prevent
> rejections.
### Accepted File Types
Use the `accept` prop to restrict file types. Accepts MIME types (`image/png`) or extensions (`.pdf`).
**Example: accepted-file-types**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import { CircleAlert, Image, Upload, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
export component AcceptedFileTypes() {
{'Upload Images (PNG and JPEG only)'}
{'Drop your images here'}{'Only PNG and JPEG files'}
component children({ context }) {
if (@context.acceptedFiles.length > 0) {
for (const file of @context.acceptedFiles; key file.name) {
}
}
if (@context.rejectedFiles.length > 0) {
for (const fileRejection of @context.rejectedFiles; key fileRejection.file.name) {
for (const error of fileRejection.errors; key error) {
{error}
}
}
}
}
}
```
### Error Handling
Set constraints with `maxFiles`, `maxFileSize`, `minFileSize`, and `accept`. Rejected files include error codes like
`TOO_MANY_FILES`, `FILE_INVALID_TYPE`, `FILE_TOO_LARGE`, or `FILE_EXISTS`.
**Example: error-handling**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import type { FileUploadFileError } from 'ark-ripple/file-upload';
import { CircleAlert, CircleCheck, File, Upload, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
const errorMessages: Record = {
TOO_MANY_FILES: 'Too many files selected (max 3 allowed)',
FILE_INVALID_TYPE: 'Invalid file type (only images and PDFs allowed)',
FILE_TOO_LARGE: 'File too large (max 1MB)',
FILE_TOO_SMALL: 'File too small (min 1KB)',
FILE_INVALID: 'Invalid file',
FILE_EXISTS: 'File already exists',
};
export component ErrorHandling() {
{'Upload Documents'}
{'Drop files here'}{'Images and PDFs, max 1MB each'}
component children({ context }) {
if (@context.acceptedFiles.length > 0) {
{'Accepted Files'}
for (const file of @context.acceptedFiles; key file.name) {
}
}
if (@context.rejectedFiles.length > 0) {
{'Rejected Files'}
for (const fileRejection of @context.rejectedFiles; key fileRejection.file.name) {
for (const error of fileRejection.errors; key error) {
{errorMessages[error as FileUploadFileError] || error}
}
}
}
}
}
```
### File Transformations
Use `transformFiles` to process files before they're added. Useful for image compression, format conversion, or
resizing.
**Example: transform-files**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import { compressAccurately } from 'image-conversion';
import { Image, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
const transformFiles = async (files: File[]) => {
return Promise.all(
files.map(async (file) => {
if (file.type.startsWith('image/')) {
try {
const blob = await compressAccurately(file, 200);
return new File([blob], file.name, { type: blob.type });
} catch (error) {
console.error('Compression failed for:', file.name, error);
return file;
}
}
return file;
}),
);
};
export component TransformFiles() {
{'Upload with Compression'}
{' Choose Images'}
component children({ context }) {
for (const file of @context.acceptedFiles; key file.name) {
}
}
}
```
### Field
Use `Field` to add helper text and error handling.
**Example: with-field**
```ripple
import { Field } from 'ark-ripple/field';
import { FileUpload } from 'ark-ripple/file-upload';
import { File, Upload, X } from 'lucide-ripple';
import field from 'styles/field.module.css';
import styles from 'styles/file-upload.module.css';
export component WithField() {
{'Attachments'}
{'Drop files here'}{'or click to browse'}
component children({ context }) {
for (const file of @context.acceptedFiles; key file.name) {
}
}
{'Upload up to 5 files'}{'Please upload at least one file'}
}
```
### Root Provider
An alternative way to control the file upload is to use the `RootProvider` component and the `useFileUpload` hook. This
way you can access the state and methods from outside the component.
**Example: root-provider**
```ripple
import { FileUpload, useFileUpload } from 'ark-ripple/file-upload';
import { File, Upload, X } from 'lucide-ripple';
import button from 'styles/button.module.css';
import styles from 'styles/file-upload.module.css';
export component RootProvider() {
const fileUpload = useFileUpload({ maxFiles: 5 });
{'File Upload'}
{'Drop files here'}{'or click to browse'}
component children({ context }) {
for (const file of @context.acceptedFiles; key file.name) {
}
}
}
```
### Pasting Files
Use `setClipboardFiles` to enable pasting images from the clipboard.
**Example: pasting-files**
```ripple
import { FileUpload, useFileUpload } from 'ark-ripple/file-upload';
import { Clipboard, X } from 'lucide-ripple';
import field from 'styles/field.module.css';
import styles from 'styles/file-upload.module.css';
export component PastingFiles() {
const fileUpload = useFileUpload({ maxFiles: 3, accept: 'image/*' });
{'Upload with Paste'}
}
```
### Media Capture
Use `capture` to access the device camera. Set to `"environment"` for back camera or `"user"` for front camera.
**Example: media-capture**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import { Camera, File, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
export component MediaCapture() {
{'Capture Photo'}
{' Open Camera'}
component children({ context }) {
for (const file of @context.acceptedFiles; key file.name) {
{file.webkitRelativePath || file.name}
}
}
}
```
### Rejected Files
Access `rejectedFiles` from the context to display validation errors.
**Example: rejected-files**
```ripple
import { FileUpload } from 'ark-ripple/file-upload';
import { CircleAlert, CircleCheck, Upload, X } from 'lucide-ripple';
import styles from 'styles/file-upload.module.css';
export component RejectedFiles() {
{
console.log('Rejected files:', details);
}}
>
{'Upload Files (Max 2)'}
{'Drop files here'}{'Maximum 2 files allowed'}
component children({ context }) {
if (@context.acceptedFiles.length > 0) {
{'Accepted Files'}
for (const file of @context.acceptedFiles; key file.name) {
}
}
if (@context.rejectedFiles.length > 0) {
{'Rejected Files'}
for (const { file, errors } of @context.rejectedFiles; key file.name) {
for (const error of errors; key error) {
{error}
}
}
}
}
}
```
## Guides
### File Previews
Use `ItemPreview` with type matching to show appropriate previews based on file format.
- `type="image/*"`: Shows image thumbnails using `ItemPreviewImage`
- `type="video/*"`: For video file previews
- `type="application/pdf"`: For PDF files
- `type=".*"`: Generic fallback for any file type
```tsx
```
### Disable Dropzone
To disable drag-and-drop functionality, set `allowDrop` to `false`.
```tsx
{/* ... */}
```
### Prevent Document Drop
By default, we prevent accidental navigation when files are dropped outside the dropzone. Set `preventDocumentDrop` to
`false` to disable this.
```tsx
{/* ... */}
```
### Prevent Double Open
Use `disableClick` on `Dropzone` when delegating clicks to a nested `Trigger`. This prevents the file picker from
opening twice.
```tsx
Choose Files
Drag files here
```
## API Reference
### Props
**Component API Reference**
**Root Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `accept` | `Record | FileMimeType | FileMimeType[]` | No | The accept file types |
| `acceptedFiles` | `File[]` | No | The controlled accepted files |
| `allowDrop` | `boolean` | No | Whether to allow drag and drop in the dropzone element |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `capture` | `'user' | 'environment'` | No | The default camera to use when capturing media |
| `defaultAcceptedFiles` | `File[]` | No | The default accepted files when rendered.
Use when you don't need to control the accepted files of the input. |
| `directory` | `boolean` | No | Whether to accept directories, only works in webkit browsers |
| `disabled` | `boolean` | No | Whether the file input is disabled |
| `ids` | `Partial<{
root: string
dropzone: string
hiddenInput: string
trigger: string
label: string
item: (id: string) => string
itemName: (id: string) => string
itemSizeText: (id: string) => string
itemPreview: (id: string) => string
itemDeleteTrigger: (id: string) => string
}>` | No | The ids of the elements. Useful for composition. |
| `invalid` | `boolean` | No | Whether the file input is invalid |
| `locale` | `string` | No | The current locale. Based on the BCP 47 definition. |
| `maxFiles` | `number` | No | The maximum number of files |
| `maxFileSize` | `number` | No | The maximum file size in bytes |
| `minFileSize` | `number` | No | The minimum file size in bytes |
| `name` | `string` | No | The name of the underlying file input |
| `onFileAccept` | `(details: FileAcceptDetails) => void` | No | Function called when the file is accepted |
| `onFileChange` | `(details: FileChangeDetails) => void` | No | Function called when the value changes, whether accepted or rejected |
| `onFileReject` | `(details: FileRejectDetails) => void` | No | Function called when the file is rejected |
| `preventDocumentDrop` | `boolean` | No | Whether to prevent the drop event on the document |
| `readOnly` | `boolean` | No | Whether the file input is read-only |
| `required` | `boolean` | No | Whether the file input is required |
| `transformFiles` | `(files: File[]) => Promise` | No | Function to transform the accepted files to apply transformations |
| `translations` | `IntlTranslations` | No | The localized messages to use. |
| `validate` | `(file: File, details: FileValidateDetails) => FileError[] | null` | No | Function to validate a file |
**Root Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | root |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
| `[data-dragging]` | Present when in the dragging state |
**ClearTrigger Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**ClearTrigger Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | clear-trigger |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
**Dropzone Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `disableClick` | `boolean` | No | Whether to disable the click event on the dropzone |
**Dropzone Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | dropzone |
| `[data-invalid]` | Present when invalid |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
| `[data-dragging]` | Present when in the dragging state |
**HiddenInput Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**ItemDeleteTrigger Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**ItemDeleteTrigger Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | item-delete-trigger |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
| `[data-type]` | The type of the item |
**ItemGroup Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `type` | `ItemType` | No | |
**ItemGroup Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | item-group |
| `[data-disabled]` | Present when disabled |
| `[data-type]` | The type of the item |
**ItemName Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**ItemName Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | item-name |
| `[data-disabled]` | Present when disabled |
| `[data-type]` | The type of the item |
**ItemPreviewImage Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**ItemPreviewImage Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | item-preview-image |
| `[data-disabled]` | Present when disabled |
| `[data-type]` | The type of the item |
**ItemPreview Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `type` | `string` | No | The file type to match against. Matches all file types by default. |
**ItemPreview Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | item-preview |
| `[data-disabled]` | Present when disabled |
| `[data-type]` | The type of the item |
**Item Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `file` | `File` | Yes | |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**Item Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | item |
| `[data-disabled]` | Present when disabled |
| `[data-type]` | The type of the item |
**ItemSizeText Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**ItemSizeText Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | item-size-text |
| `[data-disabled]` | Present when disabled |
| `[data-type]` | The type of the item |
**Label Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**Label Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | label |
| `[data-disabled]` | Present when disabled |
| `[data-required]` | Present when required |
**RootProvider Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseFileUploadReturn` | Yes | |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**Trigger Props:**
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
**Trigger Data Attributes:**
| Attribute | Value |
|-----------|-------|
| `[data-scope]` | file-upload |
| `[data-part]` | trigger |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
| `[data-invalid]` | Present when invalid |
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `dragging` | `boolean` | Whether the user is dragging something over the root element |
| `focused` | `boolean` | Whether the user is focused on the dropzone element |
| `disabled` | `boolean` | Whether the file input is disabled |
| `readOnly` | `boolean` | Whether the file input is in read-only mode |
| `transforming` | `boolean` | Whether files are currently being transformed via `transformFiles` |
| `maxFilesReached` | `boolean` | Whether the maximum number of files has been reached |
| `remainingFiles` | `number` | The number of files that can still be added |
| `openFilePicker` | `VoidFunction` | Function to open the file dialog |
| `deleteFile` | `(file: File, type?: ItemType) => void` | Function to delete the file from the list |
| `acceptedFiles` | `File[]` | The accepted files that have been dropped or selected |
| `rejectedFiles` | `FileRejection[]` | The files that have been rejected |
| `setFiles` | `(files: File[]) => void` | Sets the accepted files |
| `clearFiles` | `VoidFunction` | Clears the accepted files |
| `clearRejectedFiles` | `VoidFunction` | Clears the rejected files |
| `getFileSize` | `(file: File) => string` | Returns the formatted file size (e.g. 1.2MB) |
| `createFileUrl` | `(file: File, cb: (url: string) => void) => VoidFunction` | Returns the preview url of a file.
Returns a function to revoke the url. |
| `setClipboardFiles` | `(dt: DataTransfer) => boolean` | Sets the clipboard files
Returns `true` if the clipboard data contains files, `false` otherwise. |