# FileUpload

> Choose files by browsing or drag and drop, with type, size and count validation.

Category: Forms · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/file-upload

Use FileUpload to attach documents, logos or spreadsheets. It validates against accept, maxSize and maxFiles before files reach your code, lists accepted files with remove buttons and reports rejected files clearly. Upload progress belongs to your own logic: render it with Progress beside the list, or set hideFileList and render your own.

## Import

```tsx
import { FileUpload, formatBytes } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Drop zone

```tsx
import { FileUpload } from '@bindshq/stance-react';

export function Basic() {
  return (
    <div style={{ width: '100%', maxWidth: 520 }}>
      <FileUpload
        accept=".pdf,.docx"
        maxSize={10 * 1024 * 1024}
        title="Drop the contract here"
        hint="PDF or DOCX, up to 10 MB"
      />
    </div>
  );
}
```

### Multiple files with limits

```tsx
import * as React from 'react';
import { FileUpload } from '@bindshq/stance-react';

export function Multiple() {
  const [files, setFiles] = React.useState<File[]>([]);
  return (
    <div style={{ display: 'grid', gap: 8, width: '100%', maxWidth: 520 }}>
      <FileUpload
        multiple
        maxFiles={5}
        accept=".csv,.xlsx"
        hint="CSV or Excel, up to 5 files"
        title="Import organisation users"
        value={files}
        onFilesChange={setFiles}
      />
      <span style={{ fontSize: 13 }}>{files.length} of 5 files selected</span>
    </div>
  );
}
```

### Compact

```tsx
import { FileUpload } from '@bindshq/stance-react';

export function Compact() {
  return (
    <FileUpload
      variant="compact"
      accept="image/*"
      buttonLabel="Upload logo"
      hint="PNG or SVG, square works best"
    />
  );
}
```

### In a form

```tsx
import { FileUpload, FormField } from '@bindshq/stance-react';

export function InForm() {
  return (
    <div style={{ width: '100%', maxWidth: 520 }}>
      <FormField
        label="Countersigned copy"
        description="Upload the version signed by both parties."
        required
      >
        <FileUpload accept=".pdf" hint="PDF only" />
      </FormField>
    </div>
  );
}
```

## API reference

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| FileUpload | `accept` | `string` | None | Accepted types, e.g. ".pdf,image/*". |
| FileUpload | `maxSize` | `number` | None | Maximum size per file in bytes. |
| FileUpload | `maxFiles` | `number` | None | Maximum number of files when multiple. |
| FileUpload | `multiple` | `boolean` | `false` | Allow several files; single mode replaces the file. |
| FileUpload | `value / defaultValue` | `File[]` | None | Controlled or initial accepted files. |
| FileUpload | `onFilesChange` | `(files: File[]) => void` | None | Called with the accepted file list. |
| FileUpload | `onReject` | `(rejections: FileRejection[]) => void` | None | Called with files that failed validation. |
| FileUpload | `title` | `ReactNode` | `'Drag files here'` | Drop zone heading. |
| FileUpload | `hint` | `ReactNode` | None | Constraints text; linked to the browse button. |
| FileUpload | `buttonLabel` | `string` | `'Browse files'` | Browse button text. |
| FileUpload | `variant` | `'dropzone' \| 'compact'` | `'dropzone'` | Drop area or button only. |
| FileUpload | `hideFileList` | `boolean` | `false` | Hide the built-in list. |
| FileUpload | `disabled / required / invalid` | `boolean` | None | State; integrates with FormField. |
| FileUpload | `name` | `string` | None | Name for native form submission of browsed files. |

## Accessibility

- Drag and drop is an enhancement: the Browse button is the keyboard and screen reader path and receives the FormField label, description and required state.
- The hidden native file input is removed from the tab order so there is only one focus stop.
- Rejected files are listed in a role="alert" region with specific reasons; additions and removals are announced in a polite live region.
- Each remove button is labelled with the file name ("Remove contract.pdf").

## Keyboard

| Keys | Action |
| --- | --- |
| `Enter` `Space` | On Browse: opens the system file picker. |
| `Tab` | Moves through the browse button and remove buttons. |

## Guidelines

**Do**

- State accepted types and the size limit in the hint.
- Keep files visible after selection so people can verify them.

**Don’t**

- Rely on drag and drop alone.
- Start uploading silently without showing progress.

## Related

- https://stanceui.com/components/progress
- https://stanceui.com/components/form-field
- https://stanceui.com/components/file-tree
