third party plugins
Tailwind CSS File Upload
File Upload enhances web forms with a Tailwind CSS interface, offering drag-and-drop, previews, progress bars, multiple uploads, and file validation.
Below are the comprehensively outlined steps you can follow to seamlessly integrate Dropzone JS with FlyonUI.
Prefer to create your own style? Here is a completely unstylized example.
<div data-file-upload='{
"url": "/upload"
}'>
<template data-file-upload-preview>
<button type="button" data-file-upload-remove>
Delete!
</button>
<span data-file-upload-file-icon>
<img class="hidden" data-dz-thumbnail />
</span>
<div>
<span data-file-upload-file-name></span>.<span data-file-upload-file-ext></span>
</div>
<div data-file-upload-file-size></div>
<div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" data-file-upload-progress-bar>
<div style="width: 0" data-file-upload-progress-bar-pane></div>
</div>
<span data-file-upload-progress-bar-value>0</span>%
</template>
<div class="cursor-pointer" data-file-upload-trigger>
Drop your file here or browse
</div>
<div data-file-upload-previews></div>
</div>
Using the most basic file upload markup, here’s how file upload look.
Use <template>
to style your file upload output. The following example demonstrates the default template layout.
Choose a file with a size up to 2MB.
or
Browse
<div
data-file-upload='{
"url": "/upload",
"extensions": {
"csv": {
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4\"/><path d=\"M14 2v4a2 2 0 0 0 2 2h4\"/><path d=\"m5 12-3 3 3 3\"/><path d=\"m9 18 3-3-3-3\"/></svg>",
"class": "shrink-0 size-5"
}
}
}' >
<div class="bg-base-200/60 rounded-box flex flex-col justify-center border-2 border-base-content/20 border-dashed" >
<div class="text-center cursor-pointer p-12" data-file-upload-trigger="">
<p class="text-base-content/50 mb-3 text-sm">Choose a file with a size up to 2MB.</p>
<button class="btn btn-soft btn-sm btn-primary text-nowrap"> <span class="icon-[tabler--file-upload] size-4.5 shrink-0"></span> Drag & Drop to Upload </button>
<p class="text-base-content/50 my-2 text-xs">or</p>
<p class="link link-animated link-primary font-medium text-sm">Browse</p>
</div>
<div class="mx-12 mb-8 space-y-2 empty:m-0" data-file-upload-previews=""></div>
</div>
</div>
The system will throw an error if the file size exceeds 1 MB.
.
Choose a file no larger than 1MB.
or
Browse
<div
id="file-upload-limit"
data-file-upload='{
"url": "/upload",
"maxFilesize": 1,
"extensions": {
"csv": {
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4\"/><path d=\"M14 2v4a2 2 0 0 0 2 2h4\"/><path d=\"m5 12-3 3 3 3\"/><path d=\"m9 18 3-3-3-3\"/></svg>",
"class": "shrink-0 size-5"
}
}
}' >
<template data-file-upload-preview="">
<div class="rounded-box bg-base-100 shadow-base-300/20 p-3 shadow-lg">
<div class="mb-1 flex items-center justify-between">
<div class="flex items-center gap-x-3">
<span class="text-base-content/80 border-base-content/20 flex size-8 items-center justify-center rounded-lg border p-0.5" data-file-upload-file-icon="" >
<img class="hidden rounded-md" data-dz-thumbnail="" />
</span>
<div>
<p class="text-base-content text-sm font-medium">
<span class="inline-block truncate align-bottom" data-file-upload-file-name=""></span>
.
<span data-file-upload-file-ext=""></span>
</p>
<p class="text-base-content/50 text-xs" data-file-upload-file-size="" data-file-upload-file-success=""></p>
<p class="text-error text-xs" style="display: none" data-file-upload-file-error="">
File exceeds size limit.
</p>
</div>
</div>
<div class="flex items-center">
<div class="tooltip [--placement:top]" style="display: none" data-file-upload-file-error="">
<button type="button" class="tooltip-toggle btn btn-sm btn-circle btn-text btn-error">
<span class="icon-[tabler--alert-circle] size-4 shrink-0"></span>
</button>
<span class="tooltip-content tooltip-shown:opacity-100 tooltip-shown:visible" role="tooltip">
<span class="tooltip-body">Please try to upload a file smaller than 1MB.</span>
</span>
</div>
<button type="button" class="btn btn-sm btn-circle btn-text" data-file-upload-reload="">
<span class="icon-[tabler--refresh] size-4 shrink-0"></span>
</button>
<button type="button" class="btn btn-sm btn-circle btn-text" data-file-upload-remove="">
<span class="icon-[tabler--trash] size-4 shrink-0"></span>
</button>
</div>
</div>
<div class="flex items-center gap-x-3 whitespace-nowrap">
<div class="progress h-2" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" data-file-upload-progress-bar="" >
<div class="progress-bar progress-primary file-upload-complete:progress-success transition-all duration-500" style="width: 0" data-file-upload-progress-bar-pane="" ></div>
</div>
<span class="text-base-content mb-0.5 text-sm">
<span data-file-upload-progress-bar-value="">0</span>%
</span>
</div>
</div>
</template>
<div class="bg-base-200/60 rounded-box flex flex-col justify-center border-2 border-base-content/20 border-dashed" >
<div class="text-center cursor-pointer p-12" data-file-upload-trigger="">
<p class="text-base-content/50 mb-3 text-sm">Choose a file no larger than 1MB.</p>
<button class="btn btn-soft btn-sm btn-primary text-nowrap"> <span class="icon-[tabler--file-upload] size-4.5 shrink-0"></span> Drag & Drop to Upload </button>
<p class="text-base-content/50 my-2 text-xs">or</p>
<p class="link link-animated link-primary font-medium text-sm">Browse</p>
</div>
<div class="mx-12 mb-8 space-y-2 empty:m-0" data-file-upload-previews=""></div>
</div>
</div>
<script>
;(function () {
const { element } = HSFileUpload.getInstance('#file-upload-limit', true)
element.dropzone.on('error', (file, response) => {
if (file.size > element.concatOptions.maxFilesize * 1024 * 1024) {
const filePreview = file.previewElement
const successEls = filePreview.querySelectorAll('[data-file-upload-file-success]')
const errorEls = filePreview.querySelectorAll('[data-file-upload-file-error]')
if (successEls) successEls.forEach(el => (el.style.display = 'none'))
errorEls.forEach(el => (el.style.display = ''))
HSStaticMethods.autoInit(['tooltip'])
}
})
})()
</script>
Using file upload as a gallery.
Choose a file with a size up to 2MB.
or
Browse
<div
data-file-upload='{
"url": "/upload",
"acceptedFiles": "image/*",
"autoHideTrigger": false,
"extensions": {
"csv": {
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4\"/><path d=\"M14 2v4a2 2 0 0 0 2 2h4\"/><path d=\"m5 12-3 3 3 3\"/><path d=\"m9 18 3-3-3-3\"/></svg>",
"class": "shrink-0 size-5"
}
}
}' >
<template data-file-upload-preview="">
<div class="rounded-box bg-base-100 shadow-base-300/20 relative mt-2 p-2 shadow-lg">
<img class="mb-2 w-full rounded-lg object-cover" data-dz-thumbnail="" />
<div class="mb-1 flex items-center justify-between gap-x-3 whitespace-nowrap">
<div class="w-10">
<span class="text-base-content mb-0.5 text-sm">
<span data-file-upload-progress-bar-value="">0</span>%</span>
</div>
<div class="flex items-center gap-x-2">
<button type="button" class="btn btn-sm btn-circle btn-text" data-file-upload-remove="">
<span class="icon-[tabler--trash] size-4 shrink-0"></span>
</button>
</div>
</div>
<div class="progress h-2" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" data-file-upload-progress-bar="" >
<div class="progress-bar progress-primary file-upload-complete:progress-success transition-all duration-500" style="width: 0" data-file-upload-progress-bar-pane="" ></div>
</div>
</div>
</template>
<div class="bg-base-200/60 rounded-box flex flex-col justify-center border-2 border-base-content/20 border-dashed" >
<div class="text-center cursor-pointer p-12" data-file-upload-trigger="">
<p class="text-base-content/50 mb-3 text-sm">Choose a file with a size up to 2MB.</p>
<button class="btn btn-soft btn-sm btn-primary text-nowrap"> <span class="icon-[tabler--file-upload] size-4.5 shrink-0"></span> Drag & Drop to Upload </button>
<p class="text-base-content/50 my-2 text-xs">or</p>
<p class="link link-animated link-primary font-medium text-sm">Browse</p>
</div>
<div class="mx-12 mb-8 empty:m-0 grid grid-cols-4 gap-2 empty:gap-0 max-sm:grid-cols-2" data-file-upload-previews=""></div>
</div>
</div>
Use file upload for a single image.
<div
data-file-upload='{
"url": "/upload",
"acceptedFiles": "image/*",
"maxFiles": 1,
"singleton": true
}' >
<template data-file-upload-preview="">
<div class="size-20">
<img class="w-full rounded-full object-contain" data-dz-thumbnail="" />
</div>
</template>
<div class="flex flex-wrap items-center gap-3 sm:gap-5">
<div class="group" data-file-upload-previews="" data-file-upload-pseudo-trigger="">
<span class="border-base-content/30 text-base-content/50 flex size-17 shrink-0 cursor-pointer items-center justify-center rounded-box border-2 border-dotted hover:bg-base-200/60 group-has-[div]:hidden" >
<span class="icon-[tabler--user-square] size-9 shrink-0"></span>
</span>
</div>
<div class="grow">
<div class="flex items-center gap-x-2">
<button type="button" class="btn btn-primary" data-file-upload-trigger="">
<span class="icon-[tabler--upload] size-4 shrink-0"></span>
Upload photo
</button>
<button type="button" class="btn btn-outline btn-error" data-file-upload-clear="">Delete</button>
</div>
</div>
</div>
</div>
Using file upload as a simple file upload.
<div
data-file-upload='{
"url": "/upload",
"maxFiles": 1,
"singleton": true
}'
>
<template data-file-upload-preview="">
<div class="flex w-full items-center">
<span class="grow-0 overflow-hidden truncate" data-file-upload-file-name=""></span>
<span class="grow-0">.</span>
<span class="grow-0" data-file-upload-file-ext=""></span>
</div>
</template>
<button type="button" class="relative flex w-full overflow-hidden rounded-lg border border-base-content/20 text-sm focus:z-10 focus:ring-1 focus:border-primary focus:ring-primary focus:outline-none disabled:pointer-events-none disabled:opacity-50" >
<span class="h-full text-nowrap bg-primary text-primary-content rounded-s-lg px-4 py-3">Choose File</span>
<span class="group flex h-full grow overflow-hidden px-4 py-3 text-base-content" data-file-upload-previews="">
<span class="group-has-[div]:hidden">No Chosen File</span>
</span>
<span class="absolute left-0 top-0 h-full w-full" data-file-upload-trigger=""></span>
</button>
</div>
destroy
method is provided to facilitate the destruction of a file upload.Choose a file with a size up to 2MB.
or
Browse
<div
id="file-upload-to-destroy"
data-file-upload='{
"url": "/upload",
"extensions": {
"csv": {
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4\"/><path d=\"M14 2v4a2 2 0 0 0 2 2h4\"/><path d=\"m5 12-3 3 3 3\"/><path d=\"m9 18 3-3-3-3\"/></svg>",
"class": "shrink-0 size-5"
}
}
}' >
<div class="bg-base-200/60 rounded-box flex flex-col justify-center border-2 border-base-content/20 border-dashed" >
<div class="text-center cursor-pointer p-12" data-file-upload-trigger="">
<p class="text-base-content/50 mb-3 text-sm">Choose a file with a size up to 2MB.</p>
<button class="btn btn-soft btn-sm btn-primary text-nowrap"> <span class="icon-[tabler--file-upload] size-4.5 shrink-0"></span> Drag & Drop to Upload </button>
<p class="text-base-content/50 my-2 text-xs">or</p>
<p class="link link-animated link-primary font-medium text-sm">Browse</p>
</div>
<div class="mx-12 mb-8 space-y-2 empty:m-0" data-file-upload-previews=""></div>
</div>
</div>
<div class="mt-4 flex gap-3">
<button class="btn btn-primary" id="destroy-btn">Destroy</button>
<button class="btn btn-primary" id="reinit-btn" disabled>Reinitialize</button>
</div>
<script>
window.addEventListener('load', () => {
// Destroy and reinit variables
const fileUpload = document.querySelector('#file-upload-to-destroy')
const destroyBtn = document.querySelector('#destroy-btn')
const reinitBtn = document.querySelector('#reinit-btn')
// Destroy usage
destroyBtn.addEventListener('click', () => {
const { element } = HSFileUpload.getInstance(fileUpload, true)
element.destroy()
destroyBtn.setAttribute('disabled', 'disabled')
reinitBtn.removeAttribute('disabled')
})
// Reinit usage
reinitBtn.addEventListener('click', () => {
HSFileUpload.autoInit()
reinitBtn.setAttribute('disabled', 'disabled')
destroyBtn.removeAttribute('disabled')
})
})
</script>
Please understand that this component requires the Dropzone.js plugin. Most of the plugin’s options are also available in our wrapper.
PARAMETERS | DESCRIPTION | OPTIONS | DEFAULT VALUE |
---|---|---|---|
data-file-upload | Activate a File Upload by specifying on an element. | - | - |
:extensions | A list of extensions and their corresponding icons will be added to the container with the data-file-upload-file-icon selector if they match the extension name. | object | Includes markup. See the code. |
:autoHideTrigger | If the attribute value is true , then data-file-upload-trigger will disappear if at least one item is added to the upload and will appear if more than one item is not in the upload. | boolean | false |
:singleton | If the attribute value is true , the plugin will remove any previously uploaded files. This is necessary to emulate the behavior of a simple file input. | boolean | false |
Name | Description |
---|---|
data-file-upload-trigger | Specifies which element within the initialized container will serve as the clickable element. |
data-file-upload-previews | Specifies which element within the initialized container will act as a wrapper for uploaded items. |
data-file-upload-clear | Specifies which element within the initialized container will serve as the button to delete all files. |
data-file-upload-preview | Specifies which element within the initialized container will act as a template for the uploading item. This should be an HTML template element. |
data-file-upload-file-icon | Specifies which element within the uploading item will act as a wrapper for the file icon defined in the extensions property. |
data-file-upload-file-name | Specifies which element within the uploading item will act as a wrapper for the file name. |
data-file-upload-file-ext | Specifies which element within the uploading item will act as a wrapper for the file extension. |
data-file-upload-file-size | Specifies which element within the uploading item will act as a wrapper for the file size. |
data-file-upload-remove | Specifies which element within the uploading item will act as the remove button. |
data-file-upload-reload | Specifies which element within the uploading item will act as the re-upload button. |
data-file-upload-progress-bar | Specifies which element within the uploading item will act as the progress bar. |
data-file-upload-progress-bar-pane | Specifies which element within the uploading item will act as the progress bar pane, whose width will dynamically increase during the upload process. |
data-file-upload-progress-bar-value | Specifies which element within the uploading item will display the current progress value, which will dynamically increase during the upload process. |
The HSRemoveElement
object is contained within the global window
object.
METHOD | DESCRIPTION |
---|---|
PUBLIC METHODS | |
destroy() | Destroys the instance, removes generated markup (if any), removes added classes and attributes. |
STATIC METHODS | |
HSRemoveElement.getInstance(target, isInstance) | Returns the element associated to the target .
|
Destroy instance.
const { element } = HSFileUpload.getInstance('#file-upload-to-destroy', true);
const destroyBtn = document.querySelector('#destroy-btn');
destroyBtn.addEventListener('click', () => {
element.destroy();
});
Example of an event triggered when an item is successfully uploaded to the server.
const { element } = HSFileUpload.getInstance('#file-upload', true);
const { dropzone } = element;
dropzone.on('complete', () => {
console.log('Item uploaded!');
});