> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.

# FileUpload

Attaches files to a form and checks their type, size and count before accepting them — the drop Area when attaching is the main task on the screen, the "Select file" button when space is tight. Picking never uploads: files travel with the form when it is saved, and the product marks a row `loading` while it sends one. To pick a photo by clicking an avatar, see Data Display/Avatar → Click To Upload.

[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/FileUpload) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/inputs-fileupload--overview)

```tsx
import { FileUpload, FileUploadDropzone, FileUploadError } from '@wallarm-org/design-system/FileUpload';

export const Basic = () => (
  <FileUpload data-testid='file-upload'>
    <FileUploadDropzone data-analytics-id='FILE_UPLOAD_DROPZONE' />
    <FileUploadError />
    <Rows />
  </FileUpload>
);
```

## Usage

```tsx
import { FileUpload, FileUploadDropzone, FileUploadError } from '@wallarm-org/design-system/FileUpload';
```

## Examples

### Button trigger

The same picking and checks behind a "Select file" button that hugs its label, for when a 96px Area would crowd the form.

```tsx
import { FileUpload, FileUploadError, FileUploadTrigger } from '@wallarm-org/design-system/FileUpload';

export const ButtonTrigger = () => (
  <FileUpload data-testid='file-upload'>
    <FileUploadTrigger />
    <FileUploadError />
    <Rows />
  </FileUpload>
);
```

### In field

Inside `Field`: the label names the Area, and the description carries the format and the limit.

```tsx
import { Field, FieldDescription, FieldIndicator, FieldLabel } from '@wallarm-org/design-system/Field';
import { FileUpload, FileUploadDropzone, FileUploadError } from '@wallarm-org/design-system/FileUpload';

export const InField = () => (
  <Field required data-testid='field'>
    <FieldLabel>
      WASM module
      <FieldIndicator />
    </FieldLabel>
    <FieldDescription>
      Exports memory, spe_alloc, spe_free, spe_init, spe_on_phase · up to 32 MB
    </FieldDescription>
    <FileUpload data-testid='file-upload' accept='.wasm' maxFileSize={32 * MB}>
      <FileUploadDropzone>Choose a .wasm module or drop it here</FileUploadDropzone>
      <FileUploadError />
      <Rows />
    </FileUpload>
  </Field>
);
```

### Multiple

With `maxFiles` above one the picker stays and disables once the limit is reached, while each file stacks below it as its own row.

```tsx
import { FileUpload, FileUploadDropzone, FileUploadError } from '@wallarm-org/design-system/FileUpload';

export const Multiple = () => (
  <FileUpload
    data-testid='file-upload'
    maxFiles={5}
    defaultValue={[sample('rules.lua'), sample('headers.lua')]}
  >
    <FileUploadDropzone />
    <FileUploadError />
    <Rows />
  </FileUpload>
);
```

### Single file chosen

One file allowed and chosen: the file shows below the picker, and a new pick, or Replace in the row, swaps it.

```tsx
import { FileUpload, FileUploadDropzone, FileUploadError } from '@wallarm-org/design-system/FileUpload';

export const SingleFileChosen = () => (
  <FileUpload data-testid='file-upload' defaultValue={[sample('policy.wasm')]}>
    <FileUploadDropzone />
    <FileUploadError />
    <Rows />
  </FileUpload>
);
```

### With description

`description` adds a second line to the row for size or status — here, that the file goes with the form.

```tsx
import { FileUpload, FileUploadDropzone, FileUploadItem, FileUploadItemDeleteTrigger, FileUploadItemGroup, FileUploadItemReplaceTrigger } from '@wallarm-org/design-system/FileUpload';
import { formatFileSize } from './lib';

export const WithDescription = () => (
  <FileUpload data-testid='file-upload' defaultValue={[sample('policy.wasm')]}>
    <FileUploadDropzone />
    <FileUploadItemGroup>
      {file => (
        <FileUploadItem
          file={file}
          description={`${formatFileSize(file.size)} · uploads when you save`}
        >
          <FileUploadItemReplaceTrigger />
          <FileUploadItemDeleteTrigger />
        </FileUploadItem>
      )}
    </FileUploadItemGroup>
  </FileUpload>
);
```

### Uploading

A row marked `loading` dims and shows a spinner, and the picker locks until no row is loading; its X becomes "Cancel upload", and stopping the request is up to the product.

```tsx
import { FileUpload, FileUploadDropzone, FileUploadItem, FileUploadItemDeleteTrigger, FileUploadItemGroup, FileUploadItemReplaceTrigger } from '@wallarm-org/design-system/FileUpload';

export const Uploading = () => (
  <FileUpload data-testid='file-upload' maxFiles={5} defaultValue={[sample('rules.lua')]}>
    <FileUploadDropzone />
    <FileUploadItemGroup>
      {file => (
        <FileUploadItem file={file}>
          <FileUploadItemReplaceTrigger />
          <FileUploadItemDeleteTrigger />
        </FileUploadItem>
      )}
    </FileUploadItemGroup>
    <FileUploadItemGroup>
      <FileUploadItem file={{ name: 'headers.lua' }} loading>
        <FileUploadItemDeleteTrigger />
      </FileUploadItem>
    </FileUploadItemGroup>
  </FileUpload>
);
```

### Validation

Only `.so` / `.dylib` up to 32 KB: anything else is refused inline, naming the file and the rule.

```tsx
import { FileUpload, FileUploadDropzone, FileUploadError } from '@wallarm-org/design-system/FileUpload';

export const Validation = () => (
  <FileUpload data-testid='file-upload' accept='.so,.dylib' maxFileSize={32 * 1024}>
    <FileUploadDropzone />
    <FileUploadError />
    <Rows />
  </FileUpload>
);
```

### Disabled

Disabled: nothing responds, and the Area and the row actions dim.

```tsx
import { FileUpload, FileUploadDropzone } from '@wallarm-org/design-system/FileUpload';

export const Disabled = () => (
  <div className='flex flex-col gap-16'>
    <FileUpload data-testid='file-upload' disabled>
      <FileUploadDropzone />
    </FileUpload>
    <FileUpload disabled defaultValue={[sample('policy.wasm')]}>
      <Rows />
    </FileUpload>
  </div>
);
```

### Read only

Read-only removes the picker, Replace and Delete, while a custom `FileUploadItemAction` such as Download stays.

```tsx
import { FileUpload, FileUploadDropzone, FileUploadItem, FileUploadItemAction, FileUploadItemDeleteTrigger, FileUploadItemGroup, FileUploadItemReplaceTrigger } from '@wallarm-org/design-system/FileUpload';
import { Download } from '@wallarm-org/design-system/icons';

export const ReadOnly = () => (
  <FileUpload data-testid='file-upload' readOnly defaultValue={[sample('policy.wasm')]}>
    <FileUploadDropzone />
    <FileUploadItemGroup>
      {file => (
        <FileUploadItem file={file}>
          <FileUploadItemReplaceTrigger />
          <FileUploadItemAction aria-label={`Download ${file.name}`}>
            <Download />
          </FileUploadItemAction>
          <FileUploadItemDeleteTrigger />
        </FileUploadItem>
      )}
    </FileUploadItemGroup>
  </FileUpload>
);
```

### Stored file

A file already on the server is a row too: `FileUploadItem` works outside `FileUpload` from a name and size, and its actions only run your `onClick`.

```tsx
import { FileUploadItem, FileUploadItemAction, FileUploadItemDeleteTrigger, FileUploadItemGroup } from '@wallarm-org/design-system/FileUpload';
import { Download } from '@wallarm-org/design-system/icons';

export const StoredFile = () => (
  <FileUploadItemGroup data-testid='stored'>
    <FileUploadItem file={{ name: 'policy.wasm', size: 48 * 1024 }} description='48 KB · attached'>
      <FileUploadItemAction aria-label='Download policy.wasm'>
        <Download />
      </FileUploadItemAction>
      <FileUploadItemDeleteTrigger aria-label='Detach artifact' />
    </FileUploadItem>
  </FileUploadItemGroup>
);
```

### Long file name

Long names truncate; hover shows the full name.

```tsx
import { FileUpload } from '@wallarm-org/design-system/FileUpload';

export const LongFileName = () => (
  <FileUpload
    data-testid='file-upload'
    defaultValue={[
      sample('flow-policy-artifact-for-the-production-edge-cluster-eu-central-1.wasm'),
    ]}
  >
    <Rows />
  </FileUpload>
);
```

### Form submission

Nothing uploads on pick: the file travels in the form's `FormData` under `name`, and an empty submit of a required Field shows its error.

```tsx
import { type FormEvent, useState } from 'react';
import { Button } from '@wallarm-org/design-system/Button';
import { Field, FieldError, FieldIndicator, FieldLabel } from '@wallarm-org/design-system/Field';
import { FileUpload, FileUploadTrigger } from '@wallarm-org/design-system/FileUpload';
import { formatFileSize } from './lib';

export const FormSubmission = () => {
  const [submitted, setSubmitted] = useState<string>('');
  const onSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const file = new FormData(event.currentTarget).get('artifact');
    setSubmitted(
      file instanceof File && file.name ? `${file.name} (${formatFileSize(file.size)})` : 'nothing',
    );
  };
  return (
    <form onSubmit={onSubmit} noValidate className='flex flex-col gap-12'>
      <Field required invalid={submitted === 'nothing'}>
        <FieldLabel>
          Artifact
          <FieldIndicator />
        </FieldLabel>
        <FileUpload
          data-testid='file-upload'
          name='artifact'
          onValueChange={() => setSubmitted('')}
        >
          <FileUploadTrigger />
          <Rows />
        </FileUpload>
        {submitted === 'nothing' ? <FieldError>Attach a file</FieldError> : null}
      </Field>
      <Button type='submit' variant='outline' color='neutral' data-testid='submit'>
        Save
      </Button>
      {submitted && submitted !== 'nothing' ? (
        <p className='sb-annotation' data-testid='submitted'>
          Submitted: {submitted}
        </p>
      ) : null}
    </form>
  );
};
```

## Props

| Prop           | Type                                                         | Default | Description                                                                                                   |
| -------------- | ------------------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------- |
| value          | `File[] \| undefined`                                        |         | Controlled accepted files.                                                                                    |
| defaultValue   | `File[] \| undefined`                                        |         | Uncontrolled initial files.                                                                                   |
| onValueChange  | `((files: File[]) => void) \| undefined`                     |         | Every change to the accepted list — pick, drop, replace, delete (then `[]`).                                  |
| onFileReject   | `((rejections: FileUploadRejection[]) => void) \| undefined` |         | Files that failed validation (for consumers that also want a toast).                                          |
| accept         | `string \| string[] \| undefined`                            |         | Extensions and/or MIME types: `'.so,.dylib'`, `'image/png'`, `['.lua']`.                                      |
| maxFiles       | `number \| undefined`                                        | `1`     | Default `1`: a new pick replaces the file, and the picker stays visible, with the chosen file shown below it. |
| maxFileSize    | `number \| undefined`                                        |         | Bytes. Checked on pick, drop and replace.                                                                     |
| minFileSize    | `number \| undefined`                                        |         | Bytes.                                                                                                        |
| validate       | `((file: File) => string[] \| null) \| undefined`            |         | Custom rules; returned strings are shown verbatim as rejection messages.                                      |
| name           | `string \| undefined`                                        |         | Hidden input name — the files submit with a native form under it.                                             |
| required       | `boolean \| undefined`                                       |         |                                                                                                               |
| disabled       | `boolean \| undefined`                                       |         |                                                                                                               |
| readOnly       | `boolean \| undefined`                                       |         | Hides the picker, Delete and Replace; `FileUploadItemAction` (e.g. Download) stays.                           |
| error          | `boolean \| undefined`                                       | `false` | Error state (maps to Ark `invalid`); also read from a wrapping `Field`.                                       |
| allowDrop      | `boolean \| undefined`                                       | `true`  | Default `true`.                                                                                               |
| title          | `string \| undefined`                                        |         |                                                                                                               |
| defaultChecked | `boolean \| undefined`                                       |         |                                                                                                               |
| autoFocus      | `boolean \| undefined`                                       |         |                                                                                                               |
| id             | `string \| undefined`                                        |         |                                                                                                               |
| tabIndex       | `number \| undefined`                                        |         |                                                                                                               |
| rel            | `string \| undefined`                                        |         |                                                                                                               |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`                |         |                                                                                                               |
| data-testid    | `string \| undefined`                                        |         |                                                                                                               |

### FileUploadDropzone

| Prop           | Type                                                 | Default | Description                       |
| -------------- | ---------------------------------------------------- | ------- | --------------------------------- |
| icon           | `ReactNode`                                          |         | Replaces the default upload icon. |
| title          | `string \| undefined`                                |         |                                   |
| defaultChecked | `boolean \| undefined`                               |         |                                   |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |                                   |
| autoFocus      | `boolean \| undefined`                               |         |                                   |
| id             | `string \| undefined`                                |         |                                   |
| tabIndex       | `number \| undefined`                                |         |                                   |
| rel            | `string \| undefined`                                |         |                                   |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |                                   |
| data-testid    | `string \| undefined`                                |         |                                   |

### FileUploadError

| Prop           | Type                                                 | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| title          | `string \| undefined`                                |         |             |
| defaultChecked | `boolean \| undefined`                               |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |             |
| autoFocus      | `boolean \| undefined`                               |         |             |
| id             | `string \| undefined`                                |         |             |
| tabIndex       | `number \| undefined`                                |         |             |
| rel            | `string \| undefined`                                |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |             |
| data-testid    | `string \| undefined`                                |         |             |

### FileUploadItem

| Prop           | Type                                                 | Default | Description                                                                    |
| -------------- | ---------------------------------------------------- | ------- | ------------------------------------------------------------------------------ |
| file \*        | `FileUploadItemFile`                                 |         | A picked `File`, or a stored file described by `{ name, size? }`.              |
| description    | `ReactNode`                                          |         | Second line, e.g. "32 KB · uploads when you save".                             |
| loading        | `boolean \| undefined`                               | `false` | Uploading: dims the row, shows a spinner and locks the picker until it clears. |
| icon           | `ReactNode`                                          |         | Replaces the default file icon.                                                |
| title          | `string \| undefined`                                |         |                                                                                |
| defaultChecked | `boolean \| undefined`                               |         |                                                                                |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |                                                                                |
| autoFocus      | `boolean \| undefined`                               |         |                                                                                |
| id             | `string \| undefined`                                |         |                                                                                |
| tabIndex       | `number \| undefined`                                |         |                                                                                |
| rel            | `string \| undefined`                                |         |                                                                                |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |                                                                                |
| value          | `string \| number \| readonly string[] \| undefined` |         |                                                                                |
| data-testid    | `string \| undefined`                                |         |                                                                                |

### FileUploadItemAction

| Prop           | Type                                                                       | Default | Description |
| -------------- | -------------------------------------------------------------------------- | ------- | ----------- |
| size           | `{ small: string; medium: string; large: string; 'inline-edit': string; }` |         |             |
| loading        | `{ true: string; }`                                                        |         |             |
| fullWidth      | `{ true: string; false: string; }`                                         |         |             |
| asChild        | `boolean \| undefined`                                                     |         |             |
| data-testid    | `string \| undefined`                                                      |         |             |
| as             | `C \| undefined`                                                           |         |             |
| title          | `string \| undefined`                                                      |         |             |
| defaultChecked | `boolean \| undefined`                                                     |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined`                       |         |             |
| autoFocus      | `boolean \| undefined`                                                     |         |             |
| id             | `string \| undefined`                                                      |         |             |
| tabIndex       | `number \| undefined`                                                      |         |             |
| rel            | `string \| undefined`                                                      |         |             |
| color          | `string \| undefined`                                                      |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`                              |         |             |
| type           | `"button" \| "submit" \| "reset" \| undefined`                             |         |             |
| disabled       | `boolean \| undefined`                                                     |         |             |
| name           | `string \| undefined`                                                      |         |             |
| value          | `string \| number \| readonly string[] \| undefined`                       |         |             |
| variant        | `{ primary: string; outline: string; ghost: string; secondary: string; }`  |         |             |

### FileUploadItemDeleteTrigger

| Prop           | Type                                                                       | Default | Description |
| -------------- | -------------------------------------------------------------------------- | ------- | ----------- |
| size           | `{ small: string; medium: string; large: string; 'inline-edit': string; }` |         |             |
| loading        | `{ true: string; }`                                                        |         |             |
| fullWidth      | `{ true: string; false: string; }`                                         |         |             |
| asChild        | `boolean \| undefined`                                                     |         |             |
| data-testid    | `string \| undefined`                                                      |         |             |
| as             | `C \| undefined`                                                           |         |             |
| title          | `string \| undefined`                                                      |         |             |
| defaultChecked | `boolean \| undefined`                                                     |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined`                       |         |             |
| autoFocus      | `boolean \| undefined`                                                     |         |             |
| id             | `string \| undefined`                                                      |         |             |
| tabIndex       | `number \| undefined`                                                      |         |             |
| rel            | `string \| undefined`                                                      |         |             |
| color          | `string \| undefined`                                                      |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`                              |         |             |
| type           | `"button" \| "submit" \| "reset" \| undefined`                             |         |             |
| disabled       | `boolean \| undefined`                                                     |         |             |
| name           | `string \| undefined`                                                      |         |             |
| value          | `string \| number \| readonly string[] \| undefined`                       |         |             |
| variant        | `{ primary: string; outline: string; ghost: string; secondary: string; }`  |         |             |

### FileUploadItemGroup

| Prop           | Type                                                 | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| title          | `string \| undefined`                                |         |             |
| defaultChecked | `boolean \| undefined`                               |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |             |
| autoFocus      | `boolean \| undefined`                               |         |             |
| id             | `string \| undefined`                                |         |             |
| tabIndex       | `number \| undefined`                                |         |             |
| rel            | `string \| undefined`                                |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |             |
| data-testid    | `string \| undefined`                                |         |             |

### FileUploadItemReplaceTrigger

| Prop           | Type                                                                       | Default | Description |
| -------------- | -------------------------------------------------------------------------- | ------- | ----------- |
| size           | `{ small: string; medium: string; large: string; 'inline-edit': string; }` |         |             |
| loading        | `{ true: string; }`                                                        |         |             |
| fullWidth      | `{ true: string; false: string; }`                                         |         |             |
| asChild        | `boolean \| undefined`                                                     |         |             |
| data-testid    | `string \| undefined`                                                      |         |             |
| as             | `C \| undefined`                                                           |         |             |
| title          | `string \| undefined`                                                      |         |             |
| defaultChecked | `boolean \| undefined`                                                     |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined`                       |         |             |
| autoFocus      | `boolean \| undefined`                                                     |         |             |
| id             | `string \| undefined`                                                      |         |             |
| tabIndex       | `number \| undefined`                                                      |         |             |
| rel            | `string \| undefined`                                                      |         |             |
| color          | `string \| undefined`                                                      |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`                              |         |             |
| type           | `"button" \| "submit" \| "reset" \| undefined`                             |         |             |
| disabled       | `boolean \| undefined`                                                     |         |             |
| name           | `string \| undefined`                                                      |         |             |
| value          | `string \| number \| readonly string[] \| undefined`                       |         |             |
| variant        | `{ primary: string; outline: string; ghost: string; secondary: string; }`  |         |             |

### FileUploadTrigger

| Prop           | Type                                                                       | Default | Description |
| -------------- | -------------------------------------------------------------------------- | ------- | ----------- |
| size           | `{ small: string; medium: string; large: string; 'inline-edit': string; }` |         |             |
| loading        | `{ true: string; }`                                                        |         |             |
| fullWidth      | `{ true: string; false: string; }`                                         |         |             |
| asChild        | `boolean \| undefined`                                                     | `false` |             |
| data-testid    | `string \| undefined`                                                      |         |             |
| as             | `C \| undefined`                                                           |         |             |
| title          | `string \| undefined`                                                      |         |             |
| defaultChecked | `boolean \| undefined`                                                     |         |             |
| defaultValue   | `string \| number \| readonly string[] \| undefined`                       |         |             |
| autoFocus      | `boolean \| undefined`                                                     |         |             |
| id             | `string \| undefined`                                                      |         |             |
| tabIndex       | `number \| undefined`                                                      |         |             |
| rel            | `string \| undefined`                                                      |         |             |
| color          | `string \| undefined`                                                      |         |             |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`                              |         |             |
| type           | `"button" \| "submit" \| "reset" \| undefined`                             |         |             |
| disabled       | `boolean \| undefined`                                                     |         |             |
| name           | `string \| undefined`                                                      |         |             |
| value          | `string \| number \| readonly string[] \| undefined`                       |         |             |
| variant        | `{ primary: string; outline: string; ghost: string; secondary: string; }`  |         |             |
