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.
- 1Step 1: Installation
Install
Dropzone
andlodash
using npm. Certain JavaScript helpers for Dropzone require the lodash plugin, so make sure to install it as well.npm i dropzone lodash
- 2Step 2: Include Dropzone and Lodash JavaScript and CSS
Include the necessary CSS and JavaScript files in your project at the specified locations:
<body> <script src="../path/to/lodash/lodash.js"></script> <script src="../path/to/dropzone/dist/dropzone-min.js"></script> </body>
Since no custom CSS overrides are required, you can also use a CDN link.Insert the following JavaScript and CSS CDN links into their respective sections:
<body> <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> <script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script> </body>
Class Name | Type | Description |
---|---|---|
file-upload-complete:{tw-utility-class} | Modifier | A modifier that allows you to set Tailwind classes to items that successfully uploaded. |
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.
Pick a file up to 2MB.
<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="border-base-content/20 bg-base-100 rounded-box flex cursor-pointer justify-center border border-dashed p-12" data-file-upload-trigger="" >
<div class="text-center">
<span class="bg-base-200/80 text-base-content inline-flex size-16 items-center justify-center rounded-full">
<span class="icon-[tabler--upload] size-6 shrink-0"></span>
</span>
<div class="mt-4 flex flex-wrap justify-center">
<span class="text-base-content pe-1 text-base font-medium">Drop your file here or</span>
<span class="link link-animated link-primary font-semibold">browse</span>
</div>
<p class="text-base-content/50 mt-1 text-xs">Pick a file up to 2MB.</p>
</div>
</div>
<div class="mt-4 space-y-2 empty:mt-0" data-file-upload-previews=""></div>
</div>
The system will throw an error if the file size exceeds 1 MB.
.
File exceeds size limit.
Pick a file up to 1MB.
<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 p-4 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="border-base-content/20 bg-base-100 rounded-box flex cursor-pointer justify-center border border-dashed p-12" data-file-upload-trigger="" >
<div class="text-center">
<span class="bg-base-200/80 text-base-content inline-flex size-16 items-center justify-center rounded-full">
<span class="icon-[tabler--upload] size-6 shrink-0"></span>
</span>
<div class="mt-4 flex flex-wrap justify-center">
<span class="text-base-content pe-1 text-base font-medium">Drop your file here or</span>
<span class="link link-animated link-primary font-semibold">browse</span>
</div>
<p class="text-base-content/50 mt-1 text-xs">Pick a file up to 1MB.</p>
</div>
</div>
<div class="mt-4 space-y-2 empty:mt-0" data-file-upload-previews=""></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.
Pick a file up to 2MB.
<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="relative mt-2 rounded-box shadow-md bg-base-100 p-2" >
<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="border-base-content/20 bg-base-100 rounded-box flex cursor-pointer justify-center border border-dashed p-12" data-file-upload-trigger="" >
<div class="text-center">
<span class="bg-base-200/80 text-base-content inline-flex size-16 items-center justify-center rounded-full">
<span class="icon-[tabler--upload] size-6 shrink-0"></span>
</span>
<div class="mt-4 flex flex-wrap justify-center">
<span class="text-base-content pe-1 text-base font-medium">Drop your file here or</span>
<span class="link link-animated link-primary font-semibold">browse</span>
</div>
<p class="text-base-content/50 mt-1 text-xs">Pick a file up to 2MB.</p>
</div>
</div>
<div class="grid grid-cols-4 gap-2 empty:gap-0 max-sm:grid-cols-2" data-file-upload-previews=""></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-20 shrink-0 cursor-pointer items-center justify-center rounded-full border-2 border-dotted hover:bg-base-200/60 group-has-[div]:hidden" >
<span class="icon-[tabler--user-circle] 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-secondary" 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-base-200 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.Pick a file up to 2MB.
<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="border-base-content/20 bg-base-100 rounded-box flex cursor-pointer justify-center border border-dashed p-12" data-file-upload-trigger="" >
<div class="text-center">
<span class="bg-base-200/80 text-base-content inline-flex size-16 items-center justify-center rounded-full">
<span class="icon-[tabler--upload] size-6 shrink-0"></span>
</span>
<div class="mt-4 flex flex-wrap justify-center">
<span class="text-base-content pe-1 text-base font-medium">Drop your file here or</span>
<span class="link link-animated link-primary font-semibold">browse</span>
</div>
<p class="text-base-content/50 mt-1 text-xs">Pick a file up to 2MB.</p>
</div>
</div>
<div class="mt-4 space-y-2 empty:mt-0" data-file-upload-previews=""></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!');
});