File dropzone
Drag-and-drop file upload built on a native file input, with type and size limits, image previews and a removable file list.
How it works
A bsFileDropzone wraps a visually hidden <input type="file">, so it is one tab stop: Enter or Space opens the file dialog, and so does a click anywhere in the zone. Files can also be dropped on it, folders included. Selected files are listed under the zone with their size and a remove button, and additions are announced to screen readers. Its value is a File[]; bind it with [(value)], [formControl], [(ngModel)] or [formField]. Set multiple to keep adding files.
Selected: 0
<div bsFileDropzone multiple aria-label="Files" [(value)]="files"></div>
<p class="mt-3 mb-0 fg-3">Selected: {{ files().length }}</p>File types and limits
Set accept like the native attribute, with MIME types, wildcards such as image/* or extensions. Dropped files are checked against it too. maxFiles, maxFileSize and maxTotalSize (in bytes) and maxNameLength limit the selection. Files that don’t fit are left out, explained under the zone and announced, and (rejected) emits them with the reason. Images get a preview; set [thumbnails]="false" to show a file icon instead.
Up to 3 images of 2 MB each. Last rejected: 0
<div
bsFileDropzone
multiple
aria-label="Photos"
accept="image/*"
[maxFiles]="3"
[maxFileSize]="2_000_000"
[maxNameLength]="40"
[(value)]="photos"
(rejected)="rejectedCount.set($event.length)"></div>
<p class="mt-3 mb-0 fg-3">Up to 3 images of 2 MB each. Last rejected: {{ rejectedCount() }}</p>Single file
Without multiple, the dropzone holds one file and a new one replaces it.
<div bsFileDropzone aria-label="CV" accept=".pdf,.doc,.docx" [maxTotalSize]="5_000_000"></div>Sizes
Set size to sm or lg to change the padding.
<div class="d-flex flex-column gap-3">
<div bsFileDropzone size="sm" aria-label="Small dropzone"></div>
<div bsFileDropzone aria-label="Medium dropzone"></div>
<div bsFileDropzone size="lg" aria-label="Large dropzone"></div>
</div>Custom content and file list
Content inside the dropzone replaces its icon and text. Export it with #dropzone="bsFileDropzone" to read isDragging() or call remove(), clear() and open(). Set [fileList]="false" to render the files yourself.
<div
bsFileDropzone
#dropzone="bsFileDropzone"
multiple
size="lg"
aria-label="Project files"
[fileList]="false"
[(value)]="files">
<span class="fs-xl fw-semibold">
{{ dropzone.isDragging() ? 'Let go to upload' : 'Upload project files' }}
</span>
<span class="fg-3">Drop them here, or click to pick them from your device.</span>
</div>
@if (files().length) {
<table class="table mt-3 mb-0">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Type</th>
<th scope="col"><span class="visually-hidden">Actions</span></th>
</tr>
</thead>
<tbody>
@for (file of files(); track file) {
<tr>
<td>{{ file.name }}</td>
<td>{{ file.type || 'Unknown' }}</td>
<td class="text-end">
<button
type="button"
bsButton="subtle"
theme="danger"
size="sm"
(click)="dropzone.remove(file)">
Remove <span class="visually-hidden">{{ file.name }}</span>
</button>
</td>
</tr>
}
</tbody>
</table>
}Disabled
Disable the dropzone, or its form control. It leaves the tab order and ignores drops.
<div bsFileDropzone multiple disabled aria-label="Disabled dropzone"></div>With forms
In a bsFormField, the label points to the file input, the help text describes it, and the zone turns red with its feedback once it is touched and invalid. Validators.required fails while no file is selected. The native input always holds the selected files, so with name a plain <form> can submit them, and required works with native validation in a bsValidate form.
<form (submit)="send($event)">
<div bsFormField>
<label bsFormLabel>Attachments</label>
<div bsFileDropzone multiple [maxFiles]="5" [formControl]="attachments"></div>
<div bsInvalidFeedback>Attach at least one file.</div>
<small bsFormText>Up to 5 files.</small>
</div>
<button type="submit" bsButton theme="primary" class="mt-3" [disabled]="attachments.invalid">
Send
</button>
</form>
@if (submitted()) {
<p class="mt-3 mb-0 fg-3">Sent {{ attachments.value.length }} files.</p>
}API
BsFileDropzone
Selector: div[bsFileDropzone] · Exported as: bsFileDropzone
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
value | File[] | [] | The selected files. Supports [(value)]. |
multiple | boolean | false | Allows more than one file. Without it, a new file replaces the selected one. |
accept | string | '' | Accepted file types: MIME types, wildcards such as image/* or extensions. |
maxFiles | number | null | null | Maximum number of files. |
maxFileSize | number | null | null | Maximum size of each file, in bytes. |
maxTotalSize | number | null | null | Maximum size of all files together, in bytes. |
maxNameLength | number | null | null | Maximum length of a file name, in characters. |
required | boolean | false | Sets required on the native input, for native validation. |
name | string | null | null | Name of the native input, so a native form submits the files. |
capture | 'user' | 'environment' | null | null | Camera to use on mobile devices. |
fileList | boolean | true | Lists the selected files under the zone. |
thumbnails | boolean | true | Shows a preview of selected images in the file list. |
size | 'sm' | 'md' | 'lg' | 'md' | Padding of the zone. |
disabled | boolean | false | Disables the dropzone. Forms set this through disable(). |
aria-label | string | null | null | Accessible name of the input, when it has no visible label. |
removeLabel | string | 'Remove' | Start of the remove buttons’ accessible names, followed by the file name. |
fileListLabel | string | 'Selected files' | Accessible name of the file list. |
formatSize | (bytes: number) => string | “1.5 MB” | Formats file sizes in the file list and messages. |
rejectionText | (rejection: BsFileRejection) => string | “a.txt isn’t an accepted file type.” | Text shown and announced for a file that wasn’t added. |
validation | 'auto' | 'valid' | 'invalid' | 'none' | 'auto' | Validation styles. auto follows the bound Angular form control. |
Outputs
| Name | Type | Description |
|---|---|---|
valueChange | File[] | Emits the files when they change. |
rejected | BsFileRejection[] | Emits the files left out of a selection or drop, with a reason (type, size, total-size, count or name-length) and the limit. |
Methods
| Name | Type | Description |
|---|---|---|
addFiles(files) | BsFileRejection[] | Adds files as if they were dropped, and returns the rejected ones. |
remove(file) | void | Removes a selected file. |
clear() | void | Removes every file and clears the messages. |
open() | void | Opens the browser’s file dialog. |
focus() | void | Focuses the input. |
isDragging() | boolean | Whether files are being dragged over the zone. |
rejections() | readonly BsFileRejection[] | Files left out of the last selection or drop. |