- File Upload Hds
- Component Overview
- Key Features
- The FileManifest Object
- Reactivity Design
- Helper Instance Methods
- Summary
- Examples
- Capture event: single file being added
- Capture event: multiple files being added
- Capture event: file being removed
- Upload - fetch API
- Upload - XMLHttpRequest API
- Setting file metadata: Set error and error message
- Getting file content: as Data URL
- Getting file content: as Array Buffer
- Small File Upload
- Large File Upload
- Disabled
- Restricting allowed file types
- full-screen-drop
- Access files on the sl-file-upload component
- Slots
- Properties
- Events
- Methods
- Custom Properties
- Parts
- Dependencies
File Upload Hds
<sl-file-upload-hds> | SlFileUploadHds
A file uploader allows users to upload and attach one or more files. It shows the upload progress and status of each file.
Component Overview
The sl-file-upload
component is a Shoelace.js file uploader designed to simplify file selection
and management in web applications. It supports both drag-and-drop and traditional file input, and it
provides a reactive design that automatically updates the user interface as file states change.
Key Features
-
Multiple File Support:
Allows users to select or drag-and-drop multiple files, with built-in limits for file count and size. -
Event-Driven Updates:
The component emits events when files are added or removed, making it easy for application developers to hook into these actions. -
Reactive File Management:
Internally, each file is represented by aFileManifest
object—a wrapper that enriches the originalFile
with additional metadata. Changes to key properties on eachFileManifest
(such as upload progress, status, or image preview) are automatically reflected in the UI.
The FileManifest Object
interface FileManifest { file: File; status: 'pending' | 'loading' | 'loaded' | 'error'; progress: number; error?: string; preview?: string; }
The FileManifest object encapsulates a single file and additional data needed for reactivity. Its key attributes include:
-
file:
The original file selected by the user. -
status:
A string indicating the file’s current state (e.g.,pending
,loading
,loaded
, orerror
). -
progress:
A numeric value between 0 and 100 representing the file’s upload or processing progress. This value is managed by the application developer, allowing custom control over progress indicators. -
error (optional):
A string containing an error message if file processing fails. -
preview (optional):
An image preview (typically a data URL) for image files. This allows the component to display a thumbnail without reloading the file.
Reactivity Design
The component’s reactivity is built around the following principles:
-
Automatic UI Synchronization:
EachFileManifest
is wrapped in a reactive layer. When properties likestatus
,progress
,preview
, orerror
are modified, the component’s UI updates automatically. For example, if an application developer adjusts theprogress
of a file, any progress bar in the UI will update immediately to reflect the new value. -
Direct Developer Control:
Developers can access the list of files through a public getter (e.g.,get files()
returns an array ofFileManifest
objects). They can modify the properties of each manifest directly, and those changes are seamlessly synchronized with the UI without additional code. -
Efficient Removal:
When a file manifest is removed (via a public method likeremoveFile()
), it is detached from the reactive system. This ensures that any further changes to a removed file manifest do not trigger UI updates, maintaining both performance and a consistent state.
Helper Instance Methods
The component provides two helper instance methods to facilitate file content reading:
-
readFileAsDataURL(fileManifest: FileManifest): Promise
: Reads the file as a Data URL—a Base64-encoded string with a data URL prefix. This is useful for displaying image previews or embedding file content in the UI. -
readFileAsArrayBuffer(fileManifest: FileManifest): Promise
: Reads the file as an ArrayBuffer, which represents the raw binary data. This is useful for uploading the file to a backend or for binary processing.
These methods allow developers to retrieve the file content in the format they need, without interfering with the component’s reactivity.
Summary
The sl-file-upload
component combines a user-friendly interface with a reactive design. By
representing each file as a FileManifest
object, it provides:
- A clear and extensible way to manage file metadata.
- Automatic UI updates when key properties change.
- Developer-friendly APIs to add, edit, and remove files, while keeping the internal reactivity isolated from external manipulation.
This design ensures that the file upload process is efficient, responsive, and easy to integrate into your application.
Examples
Capture event: single file being added
Open browser console
Add a file or files to this component.
Observe console log for output.
<sl-file-upload-hds id="sfuAddFile" class="my-style"></sl-file-upload-hds> <script type="module"> const sfuAddFile = document.querySelector("sl-file-upload-hds#sfuAddFile.my-style"); sfuAddFile.addEventListener('sl-upload-add', (event) => { console.log('!! sl-upload-add emitted !!'); console.log('event.detail.manifest', event.detail.manifest); console.log('== sfuAddFile.files', sfuAddFile.files); }); </script>
Capture event: multiple files being added
Open browser console.
Add multiple files to this component.
Observe console log for output.
<sl-file-upload-hds id="sfuAddFileMulti" class="my-style"></sl-file-upload-hds> <script type="module"> const sfuAddFileMulti = document.querySelector("sl-file-upload-hds#sfuAddFileMulti.my-style"); sfuAddFileMulti.addEventListener('sl-upload-files', (event) => { console.log('!! sl-upload-files emitted !!'); console.log('> event.detail.manifest', event.detail.manifests); console.log('> sfuAddFileMulti.files', sfuAddFileMulti.files); console.log('=== Q.E.D. ==='); }); </script>
Capture event: file being removed
Open browser console,
Add a file or files to this component.
Remove a file by clicking on trash icon.
Observe console log for output.
<sl-file-upload-hds id="sfuRemoveFile" class="my-style"></sl-file-upload-hds> <script type="module"> const sfuRemoveFile = document.querySelector("sl-file-upload-hds#sfuRemoveFile.my-style"); sfuRemoveFile.addEventListener('sl-upload-remove', (event) => { console.log('!! sl-upload-remove emitted !!'); console.log('> event.detail.manifest', event.detail.manifest); console.log('> sfuRemoveFile.files', sfuRemoveFile.files); console.log('=== Q.E.D. ==='); }); </script>
Upload - fetch API
File upload with fetch browser API
This example does not have a real uploadEndpoint. if you have a working endpoint feel free to open this example on Codepen and try out!.
<sl-file-upload-hds id="sfuFetch" class="my-style"></sl-file-upload-hds> <script type="module"> const uploadEndpoint = "https://your-upload-endpoint.com/upload"; const sfuFetch = document.querySelector("sl-file-upload-hds#sfuFetch.my-style"); // Map to store active fetch requests' AbortControllers keyed by the file manifest. const activeFetch = new Map(); function uploadFile(fileManifest) { const fileManifest = uploadEvent.detail.manifest; const controller = new AbortController(); activeFetch.set(fileManifest, controller); try { // Initiate the fetch request to upload the file. // Note: Fetch API doesn't provide upload progress events. const response = await fetch(uploadEndpoint, { method: "PUT", body: fileManifest.file, signal: controller.signal }); activeFetch.delete(fileManifest); if (response.ok) { console.log("Upload successful with fetch!"); fileManifest.status = 'loaded'; } else { console.error("Upload failed:", response.statusText); sfuFetch.showError("Upload failed: " + response.statusText); fileManifest.status = 'error'; fileManifest.error = response.statusText; } } catch (error) { activeFetch.delete(fileManifest); if (error.name === 'AbortError') { console.log("Upload aborted for file:", fileManifest.file.name); } else { console.error("Network error during fetch upload:", error); sfuFetch.showError("Network error during upload. File: " + fileManifest.file.name); fileManifest.status = 'error'; fileManifest.error = "Network error"; } } } function cancelUploadFile(fileManifest) { const controller = activeFetch.get(fileManifest); if (controller) { controller.abort(); activeFetch.delete(fileManifest); } } sfuFetch.addEventListener('sl-upload-add', async (uploadEvent) => { uploadFile(uploadEvent.detail.manifest); }); sfuFetch.addEventListener('sl-upload-remove', (removeEvent) => { cancelUploadFile(removeEvent.detail.manifest); }); </script>
Upload - XMLHttpRequest API
File upload with XMLHttpRequest (XHR) browser API provides mechanism to capture file data being loaded. This
allows us to calculate progress. sl-file-upload-hds
FileManifest supplies interface to set
progress of each file. This example demonstrate javascript required to use XHR api to achieve file upload
progress.
This example does not have a real uploadEndpoint. if you have a working endpoint feel free to open this example on Codepen and try out!.
<sl-file-upload-hds id="sfuXhr" class="my-style"></sl-file-upload-hds> <script type="module"> const uploadEndpoint = "https://your-upload-endpoint.com/upload"; const sfuXhr = document.querySelector("sl-file-upload-hds#sfuXhr.my-style"); // Map to keep track of active XHR requests, keyed by the file manifest. const activeXHR = new Map(); function uploadFile(fileManifest) { const xhr = new XMLHttpRequest(); activeXHR.set(fileManifest, xhr); // store the xhr associated with this file xhr.open("PUT", uploadEndpoint, true); xhr.upload.onprogress = (event) => { if (event.lengthComputable) { const progress = (event.loaded / event.total) * 100; fileManifest.progress = progress; fileManifest.status = 'loading'; console.log(`Upload progress: ${Math.round(progress)}%`); } }; xhr.onload = () => { activeXHR.delete(fileManifest); // remove xhr from map on completion if (xhr.status === 200) { console.log("Upload successful!"); fileManifest.status = 'loaded'; } else { console.error("Upload failed:", xhr.statusText); sfuXhr.showError("Upload failed: " + xhr.statusText); fileManifest.status = 'error'; fileManifest.error = xhr.statusText; } }; xhr.onerror = () => { activeXHR.delete(fileManifest); console.error("Network error during upload."); sfuXhr.showError("Network error during upload. File: " + fileManifest.file.name); fileManifest.status = 'error'; fileManifest.error = "Network error"; }; xhr.send(fileManifest.file); }; function cancelUploadFile(fileManifest) { const xhr = activeXHR.get(fileManifest); if (xhr) { console.log("Aborting upload for file: " + fileManifest.file.name); xhr.abort(); activeXHR.delete(fileManifest); } } sfuXhr.addEventListener('sl-upload-add', (uploadEvent) => { uploadFile(uploadEvent.detail.manifest); }); sfuXhr.addEventListener('sl-upload-remove', (removeEvent) => { cancelUploadFile(removeEvent.detail.manifest); }); </script>
Setting file metadata: Set error and error message
Add a file or files to this component
Wait for 3 seconds.
observe first file being set as error’ed along error message.
<sl-file-upload-hds id="sfuSetError"></sl-file-upload-hds> <script type="module"> const sfuSetError = document.querySelector("#sfuSetError"); const errorMessage = 'The backend rejected this file because of its content.'; sfuSetError.addEventListener('sl-upload-files', (event) => { const file0 = event.detail.manifests[0]; setTimeout(() => { file0.status = 'error'; file0.error = errorMessage; }, 3000); }); </script>
Getting file content: as Data URL
The component will emit a sl-upload-files
event when a file of an appropriate type has been
dropped.
Open browser console,
Add a file or files to this component.
Observe console log for output.
<sl-file-upload-hds id="sfuDataUrl"></sl-file-upload-hds> <script type="module"> const sfuDataUrl = document.querySelector("#sfuDataUrl"); sfuDataUrl.addEventListener('sl-upload-files', async (event) => { const fileManifest = event.detail.manifests[0]; const dataUrl = await sfuDataUrl.readFileAsDataURL(fileManifest); console.log("File name: ", fileManifest.file.name); console.log("File content Data URL: ", dataUrl); }); </script>
Getting file content: as Array Buffer
The component will emit a sl-upload-files
event when a file of an appropriate type has been
dropped.
Open browser console,
Add a file or files to this component.
Observe console log for output.
<sl-file-upload-hds id="sfuArrayBuffer"></sl-file-upload-hds> <script type="module"> const sfuArrayBuffer = document.querySelector("#sfuArrayBuffer"); sfuArrayBuffer.addEventListener('sl-upload-files', async () => { const fileManifest = sfuArrayBuffer.files[0]; const arrayBuffer = await sfuArrayBuffer.readFileAsArrayBuffer(fileManifest); console.log("File name: ", fileManifest.file.name); console.log("File content array buffer: ", arrayBuffer); }); </script>
Small File Upload
<sl-file-upload-hds id="sfuSmall" upload-component-size="small"></sl-file-upload-hds>
Large File Upload
<sl-file-upload-hds id="file-upload-22" upload-component-size="large"> <slot name="large"> <div>Read more on how to <a href="#c">get the most out of bulk uploading.</a></div> </slot> </sl-file-upload-hds>
Disabled
<sl-file-upload-hds disabled></sl-file-upload-hds>
Restricting allowed file types
Use the allowed-types
attribute to control the allowed types. Note the single/double quote
pattern. This pattern is required for the deserialisation of the array.
Use the unsupported-type-message
attribute to set a custom message when an unsupported file
type is uploaded.
Only SVG and PNG files are allowed here
<sl-file-upload-hds id="branding-logo-file-upload" allowed-types='["svg", "png"]' unsupported-type-message="Only SVG and PNG files are supported here!!!!" > <p>Only SVG and PNG files are allowed here</p> </sl-file-upload-hds> <script> (() => { const fileUpload = document.querySelector('#branding-logo-file-upload'); fileUpload.addEventListener('sl-upload-files', () => { console.log("file uploaded", fileUpload.files); document.querySelector('#branding-logo-upload-complete-alert').toast(); }); })(); </script>
full-screen-drop
Enable full screen capture below
Drag any file onto this page and observe.
<script type="module" src="http://localhost:7001/dist/shoelace-autoloader.js"></script> <sl-switch id="chkFullScreenDrop" size="medium" style="margin: var(--hds-space-2x) 0">enable full screen capture</sl-switch> <!-- <sl-file-upload-hds full-screen-drop></sl-file-upload-hds> --> <sl-file-upload-hds id="sfuFullScreenDrop"></sl-file-upload-hds> <script> const chkFullScreenDrop = document.querySelector("sl-switch#chkFullScreenDrop"); const sfuFullScreenDrop = document.querySelector("sl-file-upload-hds#sfuFullScreenDrop"); chkFullScreenDrop.addEventListener( "sl-change", () => chkFullScreenDrop.checked ? sfuFullScreenDrop.setAttribute('full-screen-drop', true) : sfuFullScreenDrop.removeAttribute('full-screen-drop') ); </script>
Access files on the sl-file-upload component
Open browser console
Add a file or files to this component.
Observe console log for output.
<sl-file-upload-hds id="sfuAccessFiles" class="my-style"></sl-file-upload-hds> <script type="module"> window.addEventListener('DOMContentLoaded', async () => { await customElements.whenDefined('sl-file-upload-hds'); const sfuAccessFiles = document.querySelector("sl-file-upload-hds#sfuAccessFiles.my-style"); await sfuAccessFiles.updateComplete; const dummyContent = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." const fileOptions = { type: "text/plain", lastModified: Date.now() }; const file0 = new File([dummyContent], "file0.txt", fileOptions); const file1 = new File([dummyContent], "file1.txt", fileOptions); const file2 = new File([dummyContent], "file2.txt", fileOptions); const file3 = new File([dummyContent], "file3.txt", fileOptions); const file4 = new File([dummyContent], "file4.txt", fileOptions); sfuAccessFiles.addFile(file0); // hold onto manifest1: FileManifest as addFile() returns it const manifest1 = sfuAccessFiles.addFile(file1); // using addFiles() adding array of files sfuAccessFiles.addFiles([file2, file3, file4]); // configure manifest metadata directly via manifest object manifest1.status = 'loading'; manifest1.progress = 68.0; // accessing file manifest via sl-file-upload instance sfuAccessFiles.files[2].status = 'error'; sfuAccessFiles.files[2].error = 'Network error, unable to upload.'; sfuAccessFiles.files[3].status = 'loaded'; }); </script>
[component-metadata:sl-file-upload-hds]
Slots
Name | Description |
---|---|
(default) | The default slot. |
small
|
A slot for the small component. |
medium
|
A slot for the medium component. |
large
|
A slot for the large component. |
help
|
A slot for the help text. |
Learn more about using slots.
Properties
Name | Description | Reflects | Type | Default |
---|---|---|---|---|
allowedTypes
allowed-types
|
Allowed file types. If empty, then all supported types are allowed. |
string[]
|
[]
|
|
unsupportedTypeMessage
unsupported-type-message
|
The message to show when a file type that is not supported is dropped |
string
|
''
|
|
loadingLabel
loading-label
|
The label to show when loading a file |
string
|
'loading…'
|
|
maxFileSize
max-file-size
|
The maximum file size in MB |
number
|
5
|
|
maxFiles
max-files
|
The maximum number of files that can be uploaded |
number
|
10
|
|
disabled
|
Disables the file upload |
boolean
|
false
|
|
alertMessage
alert-message
|
Custom alert message |
string
|
'Default error message'
|
|
uploadComponentSize
upload-component-size
|
Display large, medium or small version of the component |
'small' | 'medium' | 'large'
|
'medium'
|
|
fullScreenDrop
full-screen-drop
|
Toggle full screen drop zone |
boolean
|
false
|
|
errorAlert
|
Error container |
SlAlert
|
- | |
fileInput
|
The file input element |
HTMLInputElement
|
- | |
files
|
retrieves an array of files currently loaded onto this component. |
FileManifest[]
|
- | |
updateComplete |
A read-only promise that resolves when the component has finished updating. |
Learn more about attributes and properties.
Events
Name | React Event | Description | Event Detail |
---|---|---|---|
sl-upload-add |
|
Emitted when a file of valid type is dropped. Event detail payload
event.detail.manifest
|
- |
sl-upload-remove |
|
Emitted when a file a is removed. Event detail payload event.detail.manifest |
- |
sl-upload-files |
|
Emitted when a list of files of valid type is dropped. Event detail payload
event.detail.manifests . Note that sl-upload-add will be emitted before sl-upload-files
|
- |
Learn more about events.
Methods
Name | Description | Arguments |
---|---|---|
addFiles() |
Add an array of Files. |
files: File[]
|
addFileList() |
Add a FileList. |
files: FileList
|
addFile() |
Add an individual file. Returns FileManifest , |
file: File
|
removeFile() |
Remvoe an individual file (by FileManifest). |
manifest: FileManifest
|
removeFileByIndex() |
Remvoe an individual file by index. |
index: number
|
readFileAsDataURL() |
Reads the file from the provided FileManifest as a Data URL. |
fileManifest: FileManifest
|
readFileAsArrayBuffer() |
Reads the file from the provided FileManifest as an ArrayBuffer. |
fileManifest: FileManifest
|
showError() |
Show error message |
msg: string, mst:
|
Learn more about methods.
Custom Properties
Name | Description | Default |
---|---|---|
--example |
An example CSS custom property. |
Learn more about customizing CSS custom properties.
Parts
Name | Description |
---|---|
base |
The component’s base wrapper. |
Learn more about customizing CSS parts.
Dependencies
This component automatically imports the following dependencies.
<sl-alert>
<sl-icon>
<sl-icon-button>