Works with any backend npm install Zero dependencies

Alpine.js Integration

Live Alpine.js integration using x-data directives to manage upload state reactively.

Drag & drop files here
// Alpine.js component with MultipleUpload
function fileUploader() {
 return {
 uploader: null,
 fileCount: 0,
 progress: 0,
 status: 'Ready',
 isUploading: false,
 completedFiles: [],

 init() {
 this.uploader = new MultipleUpload(this.$el, {
 uploadUrl: '/api/upload',
 autoUpload: false,
 onFileAdded: (task) => { this.fileCount++; },
 onQueueRemove: (task) => { this.fileCount = Math.max(0, this.fileCount - 1); },
 onUploadStart: () => { this.isUploading = true; this.status = 'Uploading...'; },
 onTaskProgress: (task, pct) => { this.progress = pct; },
 onTaskComplete: (task) => { this.completedFiles.push(task.name); },
 onUploadComplete: () => { this.status = 'Done!'; this.isUploading = false; }
 });
 },
 selectFiles() { this.uploader.openFileDialog(); },
 startUpload() { this.uploader.startUpload(); }
 };
}

Alpine state, uploader events

The pattern is to keep the uploader's callbacks writing into x-data: a file count, a progress number, an uploading flag. Alpine re-renders the parts of the template bound to those, and the uploader keeps ownership of its own DOM.

Initialise in x-init, after the element exists

x-init runs once the element is in the document, which is the point the uploader can attach. Keep the instance on the Alpine component object so other directives on the same element can reach it — that is what lets a button elsewhere in the template call upload() or cancel().