<div class="wf-field">
<label>Attachment</label>
<div class="wf-file" data-wf-file>
<input type="file" name="attachment" accept=".pdf,.docx">
</div>
<span class="wf-field-hint">PDF or DOCX, max 5 MB</span>
</div>
File Upload
Progressive enhancement of a native <input type="file"> via wf-file.js . The native input stays in the DOM hidden, so standard form submissions require zero changes. Three layout variants are available — inline (default), dropzone , and button — all with drag-and-drop support.
Import the module once. All [data-wf-file] wrappers are enhanced automatically on DOMContentLoaded .
Basic
Use inside a .wf-field for a labeled form field with a hint:
Inline
The default layout. Renders a trigger button alongside a name box that displays the selected filename(s). Drag-and-drop highlights the name box border on hover. When multiple is set on the native input, a drop accumulates files; single-file mode is replaced on a drop.
<!-- Single file -->
<div class="wf-file" data-wf-file>
<input type="file" name="attachment">
</div>
<!-- Multiple files -->
<div class="wf-file" data-wf-file>
<input type="file" name="attachments" multiple>
</div>
Dropzone
A large clickable drag-and-drop zone. Image preview is automatically enabled. The zone responds visually to hover, focus, and active drag states. Add data-wf-hint to show a custom hint line inside the zone.
<div class="wf-file" data-wf-file
data-wf-layout="dropzone"
data-wf-hint="PNG, JPG or GIF — max 10 MB each">
<input type="file" name="photos" multiple accept="image/*">
</div>
Button
Trigger button only. A count badge appears on the button once files are selected. Drag-and-drop highlights the button with a focus ring. Combine with data-wf-show-details="false" to suppress the file list entirely when badge feedback is enough.
<!-- Button with file list below -->
<div class="wf-file" data-wf-file data-wf-layout="button">
<input type="file" name="docs" multiple>
</div>
<!-- Button-only (suppress file list) -->
<div class="wf-file" data-wf-file data-wf-layout="button"
data-wf-show-details="false">
<input type="file" name="docs" multiple>
</div>
Sizes
Set data-wf-size to sm or lg for size presets; the default is medium. Works across all three layout variants.
<div class="wf-file" data-wf-file data-wf-size="sm">
<input type="file" name="attachment">
</div>
<div class="wf-file" data-wf-file>
<input type="file" name="attachment">
</div>
<div class="wf-file" data-wf-file data-wf-size="lg">
<input type="file" name="attachment">
</div>
Preview & File List
Add data-wf-preview to show image thumbnails in the file list (automatically on for the dropzone layout). Each list item includes a thumbnail or file icon, the filename, formatted size, and a remove button. Cap the visible list length with data-wf-max-list="N" ; a “+N more” line appears for overflow files.
<!-- Inline with image previews, list capped at 3 -->
<div class="wf-file" data-wf-file data-wf-preview data-wf-max-list="3">
<input type="file" name="photos" multiple accept="image/*">
</div>
Use data-wf-hint to add helper text below the control (or inside the dropzone). On inline and button layouts it renders as a .wf-file-hint paragraph.
<div class="wf-file" data-wf-file
data-wf-hint="PDF or DOCX, max 5 MB per file">
<input type="file" name="docs" multiple accept=".pdf,.docx">
</div>
Auto Submit
Add data-wf-auto-submit to trigger an upload the moment files are selected or dropped — no submit button required. Two integration modes are available depending on whether the .wf-file wrapper is inside a data-wf-ajax-form form or used standalone.
Mode 1 — inside a wf-ajax-form
When .wf-file is nested inside a form[data-wf-ajax-form] , adding data-wf-auto-submit calls form.requestSubmit() as soon as files arrive. The form handles the fetch, CSRF, and toast response exactly as a normal AJAX form submission — no extra configuration needed on the file wrapper.
<!-- wf-ajax-form handles the fetch; wf-file triggers it on drop/select -->
<form data-wf-ajax-form
data-wf-url="/api/upload"
data-wf-csrf-header="X-CSRF-Token">
<div class="wf-file" data-wf-file
data-wf-layout="dropzone"
data-wf-hint="Drop an image to upload instantly"
data-wf-auto-submit>
<input type="file" name="avatar" accept="image/*">
</div>
</form>
Mode 2 — standalone
When the wrapper is not inside a data-wf-ajax-form , add data-wf-url so the component uploads directly. Extra fields can be merged via data-wf-params (JSON object), a CSRF header injected with data-wf-csrf-header (reads the <meta name="csrf-token"> value), and data-wf-clear-on-success to reset the file list after a confirmed upload. Server responses follow the standard { type, title, message } format and are shown via WojoToast.
<div class="wf-file" data-wf-file
data-wf-layout="dropzone"
data-wf-hint="Drop a document to upload"
data-wf-auto-submit
data-wf-url="/api/upload"
data-wf-params='{"folder":"documents","overwrite":"true"}'
data-wf-csrf-header="X-CSRF-Token"
data-wf-clear-on-success>
<input type="file" name="document" accept=".pdf,.docx">
</div>
Listening to upload events
In standalone mode the component dispatches wf:file-upload-success and wf:file-upload-error after the server responds, in addition to showing a toast. Use these to update surrounding UI without polling.
const el = document.querySelector('[data-wf-file]')
el.addEventListener('wf:file-upload-success', e => {
console.log('Uploaded files:', e.detail.files)
console.log('Server data:', e.detail.data)
// e.detail.data is the parsed JSON response from the server
})
el.addEventListener('wf:file-upload-error', e => {
console.warn('Upload failed:', e.detail.data?.message)
})
Data Attributes
Place all attributes on the .wf-file wrapper element.
| Attribute | Values | Description |
|---|---|---|
data-wf-file |
— | Required. Marks the wrapper for auto-init on DOMContentLoaded. |
data-wf-layout |
inline · dropzone · button |
Layout variant. Default: inline. |
data-wf-size |
sm · lg |
Size preset. Omit for medium (default). |
data-wf-preview |
— | Show image thumbnails in the file list. Automatically enabled for the dropzone layout. |
data-wf-max-list |
integer | Cap file list at N visible items; a “+N more” line appears for overflow. 0 = show all. Default: 0. |
data-wf-show-details |
false |
Set to false to suppress the file list entirely. The trigger button and badge still work normally. |
data-wf-hint |
string | Hint text shown below the control (inline/button) or inside the dropzone body. |
data-wf-disabled |
— | Disables the component and mirrors disabled on the native input. |
data-wf-complete |
— | DOM mutation applied to data-wf-parent on success. Same strategies as wf-ajax-action: append | prepend | replace | innerHTML | insert | remove | reload | highlight. Server can also set response.complete to override. |
data-wf-auto-submit |
— | Upload immediately when files are selected or dropped. Delegates to the nearest data-wf-ajax-form parent if present; otherwise uses data-wf-url for a standalone fetch. |
data-wf-parent |
— | CSS selector of the element to mutate on success. |
data-wf-url |
URL string | Upload endpoint for standalone auto-submit (no data-wf-ajax-form parent). Required when using data-wf-auto-submit outside a form. |
data-wf-params |
JSON object | Extra key/value pairs merged into the upload FormData for standalone auto-submit. Example: data-wf-params='{"folder":"docs"}'. |
data-wf-csrf-header |
string | Header name for CSRF protection (e.g. X-CSRF-Token). Value is read from <meta name="csrf-token">. |
data-wf-clear-on-success |
— | Clear the file list after a successful standalone upload response. |
Native input attributes
These are placed on the <input type="file"> inside the wrapper.
| Attribute | Description |
|---|---|
multiple |
Allow multi-file selection. Drop accumulates files; single-file mode is replaced on a drop. |
accept |
Restrict the file picker and validate dropped files. Accepts MIME types (image/*), extensions (.pdf), or comma-separated combinations. |
disabled |
Also disables the component UI. |
name |
Used for form submission as normal. |
CSS Custom Properties
Override on the .wf-file element or any ancestor.
| Property | Default | Description |
|---|---|---|
--wf-file-radius |
var(--wf-radius-4) |
Border radius is applied to the name box, dropzone, and button. |
--wf-file-border |
var(--wf-border) |
Border color for the name box and dropzone outline. |
Programmatic API
After initialization the instance is stored on the wrapper element as el._wfFile .
const el = document.querySelector('[data-wf-file]')
const fs = el._wfFile
fs.addFiles(someFileList) // add files (filtered by accept)
fs.removeFile(0) // remove first file
fs.clear() // remove all files
fs.getFiles() // returns File[]
fs.setDisabled(true) // disable
fs.setShowDetails(false) // hide file list
fs.destroy() // remove listeners & revoke preview URLs
| Method | Returns | Description |
|---|---|---|
addFiles(fileList) |
— | Add files from a FileList or File[], filtered by accept. Accumulates in multiple modes; replaces it in a single mode.
|
removeFile(index) |
— | Remove the file at the given 0-based index. |
clear() |
— | Remove all files and reset the UI. |
getFiles() |
File[] |
Returns a shallow copy of the internal file list. |
setDisabled(bool) |
— | Enable or disable the component and the underlying native input. |
setShowDetails(bool) |
— | Show or hide the file list. Re-renders immediately when toggled on. |
destroy() |
— | Remove all event listeners and revoke all image preview object URLs. |
Events
All events are dispatched on the .wf-file wrapper element and bubble up the DOM.
const el = document.querySelector('[data-wf-file]')
el.addEventListener('wf:file-change', e => {
console.log('Current files:', e.detail.files)
})
el.addEventListener('wf:file-add', e => {
console.log('Added:', e.detail.added)
console.log('All files:', e.detail.files)
})
el.addEventListener('wf:file-remove', e => {
console.log('Removed:', e.detail.removed)
console.log('Remaining:', e.detail.files)
})
| Event | detail | Fired when |
|---|---|---|
wf:file-change |
{ files } |
The file list changes for any reason. files is the current File[].
|
wf:file-add |
{ files, added } |
Files were added. added is the array of newly-added File objects.
|
wf:file-remove |
{ files, removed } |
Files were removed. removed is the array of removed File objects.
|
wf:file-upload-success |
{ files, data } |
Standalone auto-submit completed successfully. data is the parsed JSON response from the server. Not fired in wf-ajax-form mode.
|
wf:file-upload-error |
{ files, data } |
Standalone auto-submit received an error response from the server. data is the parsed JSON response. Not fired in wf-ajax-form mode.
|
i18n
Register a locale globally with WojoI18n before DOMContentLoaded . Any key you omit falls back to the built-in English default. Use the token {name} in removeLabel and {n} in selectedCount .
For one-off text overrides set the data-wf-i18n-* attributes directly on the .wf-file wrapper. These take the highest priority.
import { WojoI18n } from './wf-base.js'
WojoI18n.setLocale('fr')
WojoI18n.register('fr', {
fileSelect: {
triggerLabel: 'Choisir un fichier',
triggerLabelMultiple: 'Choisir des fichiers',
noFileChosen: 'Aucun fichier sélectionné',
dropzoneLabel: 'Déposez vos fichiers ici',
dropzoneHint: 'ou cliquez pour parcourir',
draggingLabel: 'Déposez pour envoyer',
removeLabel: 'Supprimer {name}',
clearLabel: 'Tout supprimer',
selectedCount: '{n} fichiers sélectionnés',
}
})
<!-- Per-instance overrides (highest priority) -->
<div class="wf-file" data-wf-file
data-wf-i18n-trigger-label="Wählen Sie eine Datei"
data-wf-i18n-no-file-chosen="Keine Datei ausgewählt"
data-wf-i18n-dropzone-label="Dateien hier ablegen"
data-wf-i18n-selected-count="{n} Dateien ausgewählt">
<input type="file" name="upload" multiple>
</div>
i18n keys
| Key | data-wf-i18n-* attribute | Default (English) |
|---|---|---|
triggerLabel |
data-wf-i18n-trigger-label |
Choose a file |
triggerLabelMultiple |
data-wf-i18n-trigger-label-multiple |
Choose files |
noFileChosen |
data-wf-i18n-no-file-chosen |
No file is chosen |
dropzoneLabel |
data-wf-i18n-dropzone-label |
Drop files here |
dropzoneHint |
data-wf-i18n-dropzone-hint |
or click to browse |
draggingLabel |
data-wf-i18n-dragging-label |
Drop to upload |
removeLabel |
data-wf-i18n-remove-label |
Remove {name} |
clearLabel |
data-wf-i18n-clear-label |
Clear all |
selectedCount |
data-wf-i18n-selected-count |
{n} files selected |
Class Reference
All modifier classes available on .wf-file
| Class | Description |
|---|---|
wf-file |
Root wrapper element. Add data-wf-file for auto-init. Receives data-wf-layout, data-wf-size, data-wf-disabled, and data-wf-dragging data attributes set by JS.
|
wf-file-row |
Flex row inside the inline layout containing the trigger label and name box. Created by JS. |
wf-file-trigger |
The visible trigger <label> that opens the file picker. Styled as a primary button. Receives wf-btn-sm or wf-btn-lg for size variants. |
wf-file-namebox |
Filename display area in the inline layout. Receives data-wf-has-files once files are selected (changes text color from muted to normal). |
wf-file-dropzone |
Drag-and-drop zone element in the dropzone layout. Acts as a button role element with keyboard support. |
wf-file-dropzone-icon |
Cloud upload icon inside the dropzone. Animates upward on hover and drag. |
wf-file-dropzone-label |
Primary heading text inside the dropzone. Hidden during active drag. |
wf-file-dropzone-hint |
Subtext line inside the dropzone. Hidden during active drag. |
wf-file-dropzone-dragging-label |
Text shown only while a drag is active over the zone (“Drop to upload”). |
wf-file-badge |
File count badge on the trigger button in the button layout. Hidden when the count is 0. |
wf-file-hint |
Hint paragraph rendered below the control (inline/button) or inside the dropzone when data-wf-hint is set.
|
wf-file-list |
Selected-files <ul>. Hidden when the list is empty. Absent entirely when data-wf-show-details="false".
|
wf-file-item |
Individual file row <li> inside the list. Animates in addition. |
wf-file-item-thumb |
32×32 thumbnail area. Shows an <img> for images when preview is on, or a generic file icon otherwise. |
wf-file-item-details |
Container for the filename and size spans. |
wf-file-item-name |
Filename span. Truncates with ellipsis on overflow. |
wf-file-item-size |
Formatted file size span (B / KB / MB / GB). |
wf-file-item-remove |
Remove the button for an individual file item. Carries data-idx for the 0-based file index. |
wf-file-more |
Overflow indicator <li> shown when the file list exceeds data-wf-max-list (“+N more”).
|