Drop-in Filestack-style file staging for <input type="file">. Users drop files or folders, rename or remove before submit, and your backend gets a clean FormData.
goboldlyforward.github.io/uploady — drop files, a folder, or click browse, then rename / remove / re-add before submitting to see what would be posted.
- Auto-mounts on any
<input type="file">with adata-uploadyattribute. - Drag-and-drop, click-to-browse, and folder drop. Folders are recursively flattened by default; opt in to retaining relative paths with
data-retain-folders="true". - Each new file cascades into a staging list with a progress-bar animation. Click × to remove, double-click the name (or hover and click the pencil) to rename inline. Extension is preserved.
- Per-file validation against the standard
acceptattribute and againstdata-max-size(per file) /data-max-files(total). Failing files appear in an error state and are excluded from the form post. - Keeps the original
<input>in the DOM and syncs its.filesto whatever the user has staged, so a normal multipart form post Just Works — no plugin-specific knowledge needed on the receiver. - Doesn't upload anything itself. It curates the
FileListfor your form.
No framework, no build step, no backend. Drop in the CSS + JS and add an attribute.
npm install @goboldlyforward/uploadyOr grab the files directly:
<link rel="stylesheet" href="path/to/uploady.css">
<script src="path/to/uploady.js"></script><form action="/uploads" method="post" enctype="multipart/form-data">
<input type="file" name="docs" multiple
accept="image/*,.pdf"
data-uploady
data-max-files="20"
data-max-size="10485760">
<button>Submit</button>
</form>The plugin auto-inits on DOMContentLoaded.
All options are data-* attributes on the file input. Standard HTML attributes (multiple, accept) are honored.
| Attribute | Default | What it does |
|---|---|---|
data-retain-folders |
false |
Show the relative path of files dropped inside a folder. Otherwise folders are flattened. |
data-animate |
true |
Cascade each new file with a progress-bar staging animation. false drops them in instantly. |
data-allow-rename |
true |
Show a pencil icon on hover; click (or double-click the name) to rename inline. Enter commits, Escape cancels. Extension preserved. |
data-allow-remove |
true |
Show a × on each file to remove it from the staged set. |
data-max-files |
— | Cap on number of files. Extras are silently skipped with a notice. |
data-max-size |
— | Per-file byte limit. Oversized files appear in an error state and are excluded from the form post. |
accept |
— | Standard HTML attribute. Files that don't match show an error and are excluded. |
multiple |
— | Standard HTML attribute. Without it, uploady runs in single-file mode: each new pick replaces the prior file. |
// Manual init (after injecting new file inputs)
Uploady.initAll(scope); // scan a subtree for [data-uploady]
Uploady.init(inputElement); // mount one input
// Live instance
const inst = Uploady.instances.get(inputElement);
inst.getFiles(); // -> File[] (excludes errored)
inst.getEntries(); // -> [{ id, file, status, error, relPath }]
inst.addFiles(files); // programmatically add Files
inst.removeFile(id); // drop a file from the staged set
inst.renameFile(id, "new.pdf"); // rebuild the File with a new name
inst.reset(); // back to the empty dropzoneAll events bubble from the original <input>:
input.addEventListener('uploady:files-added', (e) => {
console.log(e.detail.added, e.detail.all);
});
input.addEventListener('uploady:file-removed', (e) => {
console.log(e.detail.id, e.detail.file);
});
input.addEventListener('uploady:file-renamed', (e) => {
console.log(e.detail.oldName, '->', e.detail.newName);
});
input.addEventListener('uploady:reset', () => { /* … */ });HTML, CSS, and ~23KB of JavaScript. No framework, no build step. Uses the DataTransfer API, webkitGetAsEntry for folder drop, and URL.createObjectURL for image thumbnails.
MIT — see LICENSE.