# File Upload

> Drag-and-drop zone with click-to-browse and an optional file list. Value type is `File | File[] | null`. Rejected files surface through the `(rejected)` output.

Source: https://ngwr.dev/reference/components/file-upload  
Kind: Signal Forms

## Installation

```angular-ts
import { WrFileUpload } from 'ngwr/file-upload';

@Component({ imports: [WrFileUpload] })
export class MyComponent {
  protected readonly files = signal<readonly File[] | null>(null);
}
```

## Single file

Default mode — last accepted file wins.

```angular-html
<wr-file-upload [(value)]="file" />
```

## Multiple files

`multiple` enables additive selection; `maxFiles` caps it.

```angular-html
<wr-file-upload [(value)]="files" [multiple]="true" [maxFiles]="5" />
```

## Constrained

`accept` filters by extension or MIME; `maxSize` enforces a per-file byte cap. Rejected files come through `(rejected)`.

```angular-html
<wr-file-upload
  [(value)]="avatar"
  accept=".png,.jpg,image/webp"
  [maxSize]="2 * 1024 * 1024"
  helperText="PNG, JPG or WebP, up to 2 MB"
  (rejected)="onRejected($event)"
/>
```

## Types

Data shapes used by the inputs and outputs above.

```angular-ts
interface WrFileUploadRejection {
  file: File;
  reason: WrFileUploadRejectionReason;
}

type WrFileUploadRejectionReason = 'type' | 'size' | 'count';
```

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `WrFileUploadRejection` | One rejected file, emitted via (rejected). | `interface` | — |
| `file`required | The rejected File object. | `File` | — |
| `reason`required | Why it was rejected. | `WrFileUploadRejectionReason` | — |
| `WrFileUploadRejectionReason` | Rejection cause. | `'type' \| 'size' \| 'count'` | — |

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `multiple` | Allow multiple files. Single mode replaces on each add. | `boolean` | `false` |
| `accept` | `accept` attribute — comma-separated MIME types or extensions. | `string` | `''` |
| `maxSize` | Max bytes per file. `0` disables the check. | `number` | `0` |
| `maxFiles` | Max files (multi mode only). `0` disables the check. | `number` | `0` |
| `showList` | Render the picked-files list below the zone. | `boolean` | `true` |
| `disabled` | Disable interaction. Bound automatically from the field's disabled state when used with `[formField]`. | `boolean` | `false` |
| `readonly` | Refuse changes to the selection while the zone stays focusable and the files still submit. Bound automatically from the field's readonly state when used with `[formField]`. The zone stops opening the picker, a drop is refused and the per-file remove buttons go inert — but the list stays readable, which is the point. No `aria-readonly`: the zone is a `role="button"`, and ARIA does not define the state for that role, so `aria-disabled` would be the only mirror available and it would say the wrong thing. | `boolean` | `false` |
| `pickLabel` | Primary call-to-action label. Falls back to `fileUpload.browse`. | `string \| null` | `null` |
| `dropLabel` | Secondary instruction below the CTA. Falls back to `fileUpload.dropZone`. | `string \| null` | `null` |
| `dropZoneLabel` | Drop-zone host aria-label. Falls back to `fileUpload.dropZoneLabel`. | `string \| null` | `null` |
| `removeFileLabel` | Remove-file button aria-label. Falls back to `fileUpload.removeFile`. | `string \| null` | `null` |
| `helperText` | Optional helper text shown below the labels (e.g. accepted formats). | `string` | `''` |
| `value` | Selected file(s). Bound by `[formField]`, or two-way via `[(value)]`. Shape follows `multiple`: a single `File` (or `null`), or a `File[]` array. | `File \| readonly File[] \| null` | `null` |
| `(rejected)` | Emitted when files are rejected for type / size / count reasons. | `readonly WrFileUploadRejection[]` | — |
| `(touch)` | Emitted on commit so a bound field can mark itself touched. | `void` | — |
