Upload
The Upload component allows users to select and upload files via browsing or drag-and-drop. It supports configurable file type and size validation, multiple file uploads, and provides visual feedback for each file's upload status.
Import
- React
- Angular
- Vue.js
// with @dhl-official/react-library:
import { DhlUpload } from "@dhl-official/react-library"
// with @dhl-official/ui-libraries/react-library:
import { DhlUpload } from "@dhl-official/ui-libraries/react-library"
If the DUIL has been installed, you can use the web component directly:
<dhl-upload></dhl-upload>
// with @dhl-official/vue-library:
import { DhlUpload } from "@dhl-official/vue-library"
// with @dhl-official/ui-libraries/vue-library:
import { DhlUpload } from "@dhl-official/ui-libraries/vue-library"
DhlUpload is a presentational component only — it does not send files to a server. It handles file selection, drag-and-drop, and client-side validation (size/type), but performing the actual upload request and reporting its outcome back to the component is the consumer's responsibility. See Handling File Uploads below.
File Statuses
Each entry in files has a status field (type DhlUploadFileItemStatus) that drives what dhl-upload-file-item renders. dhl-upload only ever sets the first two of these — moving a file to success/done or error after that point is done by the consumer, based on the outcome of the real upload request.
| Status | Set by | Rendered as | When to use it |
|---|---|---|---|
in-progress | dhl-upload (built-in) | Spinner + "Uploading..." | Automatically set right after a file passes client-side validation. |
error | dhl-upload (built-in) or consumer | Error icon + statusMessage | Automatically set when validation fails (size/type). Also set this yourself if your upload request fails. |
success | Consumer | Success ring + statusMessage | Set this once your upload request resolves, to show a brief confirmation state. |
done | Consumer | Static file icon + fileSize | The resting state for a completed upload, e.g. after a success confirmation or once the user clicks "Continue". |
Handling File Uploads
Since dhl-upload doesn't perform the network request itself, wire it up as follows:
- Listen for the
dhlUploadFilesAddedevent. It fires with{ files }right after client-side validation, and each new entry already hasstatus: "in-progress". - In your handler, send the file(s) to your own backend (e.g. via
fetchorXMLHttpRequest). - Once the request settles, update that file's entry back into the
filesprop withstatus: "done"on success, orstatus: "error"(with astatusMessage) on failure.filesis designed to be a controlled prop — pass your updated array back in.
Skipping step 3 is the most common integration mistake — it's what leaves the spinner from step 1 stuck permanently, since nothing else in the component will ever clear it.
- React
- Angular
- Vue.js
function InvoiceUpload() {
const [files, setFiles] = useState([]);
const updateFile = (id, patch) =>
setFiles((prev) => prev.map((f) => (f.id === id ? { ...f, ...patch } : f)));
const handleFilesAdded = async ({ files: added }) => {
setFiles(added); // shows the in-progress spinner immediately
for (const entry of added) {
if (entry.status !== "in-progress") continue; // skip files that failed validation
try {
const form = new FormData();
form.append("file", entry.file);
const res = await fetch("/api/customs-invoices", { method: "POST", body: form });
if (!res.ok) throw new Error("Upload failed");
updateFile(entry.id, { status: "done" });
} catch {
updateFile(entry.id, {
status: "error",
statusMessage: "Upload failed, please try again",
});
}
}
};
return (
<DhlUpload
acceptedTypes=".pdf,.docx,.odt,.rtf"
maxSize={31457280}
multiple
files={files}
onDhlUploadFilesAdded={handleFilesAdded}
/>
);
}
<dhl-upload
accepted-types=".pdf,.docx,.odt,.rtf"
max-size="31457280"
multiple
[files]="files"
(dhlUploadFilesAdded)="handleFilesAdded($event)"
></dhl-upload>
files: DhlUploadFile[] = [];
async handleFilesAdded(event: CustomEvent<{ files: DhlUploadFile[] }>) {
const added = event.detail.files;
this.files = added; // shows the in-progress spinner immediately
for (const entry of added) {
if (entry.status !== "in-progress") continue; // skip files that failed validation
try {
const form = new FormData();
form.append("file", entry.file);
const res = await fetch("/api/customs-invoices", { method: "POST", body: form });
if (!res.ok) throw new Error("Upload failed");
this.files = this.files.map((f) =>
f.id === entry.id ? { ...f, status: "done" } : f
);
} catch {
this.files = this.files.map((f) =>
f.id === entry.id
? { ...f, status: "error", statusMessage: "Upload failed, please try again" }
: f
);
}
}
}
<dhl-upload
accepted-types=".pdf,.docx,.odt,.rtf"
:max-size="31457280"
multiple
:files="files"
@dhl-upload-files-added="handleFilesAdded"
></dhl-upload>
const files = ref([]);
async function handleFilesAdded(event) {
const added = event.detail.files;
files.value = added; // shows the in-progress spinner immediately
for (const entry of added) {
if (entry.status !== "in-progress") continue; // skip files that failed validation
try {
const form = new FormData();
form.append("file", entry.file);
const res = await fetch("/api/customs-invoices", { method: "POST", body: form });
if (!res.ok) throw new Error("Upload failed");
files.value = files.value.map((f) =>
f.id === entry.id ? { ...f, status: "done" } : f
);
} catch {
files.value = files.value.map((f) =>
f.id === entry.id
? { ...f, status: "error", statusMessage: "Upload failed, please try again" }
: f
);
}
}
}
Always handle the failure path. If only the success branch sets a new status, a network error or timeout leaves the file stuck on in-progress indefinitely — the same symptom as never wiring up the upload at all.
Code
- React
- Angular
- Vue.js
<DhlUpload
acceptedTypes=".pdf,.docx,.odt,.rtf"
maxSize={31457280}
multiple
/>
<dhl-upload
accepted-types=".pdf,.docx,.odt,.rtf"
max-size="31457280"
multiple
></dhl-upload>
<dhl-upload
accepted-types=".pdf,.docx,.odt,.rtf"
:max-size="31457280"
multiple
></dhl-upload>
Interactive Demo
Readme
Properties
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
acceptedTypes | accepted-types | An optional prop defining a comma-separated list of accepted file types. Supports MIME types and extensions (e.g. ".pdf,.docx" or "application/pdf,image/*"). | string | DHL_UPLOAD.DEFAULTS.ACCEPTED_TYPES |
background | background | An optional prop to set the background of the component container. Defaults to transparent. | "transparent" | "white" | DHL_UPLOAD.BACKGROUND.TRANSPARENT |
browseButtonLabel | browse-button-label | An optional prop for the browse button label. | string | "Browse Files" |
cancelButtonLabel | cancel-button-label | An optional prop for the cancel button label. | string | "Cancel" |
continueButtonLabel | continue-button-label | An optional prop for the continue button label. | string | "Continue" |
dataAriaLabel | data-aria-label | An optional prop defining the text read by the screen reader. | string | "File upload" |
dataClassName | data-class-name | An optional class name prop for the component. | string | undefined |
dataId | data-id | An optional prop. Gives a valid HTML ID attribute value for the component. | string | `dhl-upload-${getRandomString()}` |
dataMaskPii | data-mask-pii | An optional prop to mask sensitive data in session-replay tools. When true, sets data-di-mask on the inner element. | boolean | undefined |
dataTestid | data-testid | An optional prop. The test id attached to the component as a data-testid attribute. | string | undefined |
description | description | An optional prop for the description text displayed in the drop zone. | string | "Or drop here to upload" |
files | files | An optional prop for the list of files rendered in the upload. The component manages this list in response to user interaction, but consumers may provide an initial value or take control by updating it externally. Each entry follows the DhlUploadFile interface. | DhlUploadFile[] | [] |
isDisabled | is-disabled | An optional flag to define if the component is disabled. | boolean | false |
maxSize | max-size | An optional prop for the maximum file size in bytes. Defaults to 30 MB. | number | DHL_UPLOAD.DEFAULTS.MAX_SIZE |
maxSizeLabel | max-size-label | An optional prop for the max size display text. If not set, the value is auto-generated from the maxSize prop. | string | undefined |
multiple | multiple | An optional flag to allow multiple file uploads. | boolean | DHL_UPLOAD.DEFAULTS.MULTIPLE |
showBorder | show-border | An optional flag to display the dashed border around the drop zone. | boolean | DHL_UPLOAD.DEFAULTS.SHOW_BORDER |
showCta | show-cta | An optional flag to display the Cancel/Continue action buttons. | boolean | DHL_UPLOAD.DEFAULTS.SHOW_CTA |
showDescription | show-description | An optional flag to display the description text. | boolean | DHL_UPLOAD.DEFAULTS.SHOW_DESCRIPTION |
showFileTypes | show-file-types | An optional flag to display the accepted file types. | boolean | DHL_UPLOAD.DEFAULTS.SHOW_FILE_TYPES |
showFiles | show-files | An optional flag to display the file list. | boolean | DHL_UPLOAD.DEFAULTS.SHOW_FILES |
showIcon | show-icon | An optional flag to display the upload icon in the drop zone. | boolean | DHL_UPLOAD.DEFAULTS.SHOW_ICON |
Events
| Event | Description | Type |
|---|---|---|
dhlUploadCancel | Event emitted when the cancel button is clicked. | CustomEvent<void> |
dhlUploadContinue | Event emitted when the continue button is clicked. | CustomEvent<{ files: DhlUploadFile[]; }> |
dhlUploadFileRemoved | Event emitted when a file is removed from the list. | CustomEvent<{ fileId: string; fileName: string; }> |
dhlUploadFilesAdded | Event emitted when files are added to the upload (after validation). | CustomEvent<{ files: DhlUploadFile[]; }> |
Dependencies
Depends on
Graph
Built by DHL User Interface Library Team!