Works with any backend npm install Zero dependencies

Extension Filter

Only allow specific file extensions using allowedExtensions.

(function() {
 var logEl = document.getElementById('log');
 function log(m) { var d = document.createElement('div'); d.className='pkg-log-entry'; d.textContent=m; logEl.appendChild(d); logEl.scrollTop=logEl.scrollHeight; }
 new MultipleUpload('#demo', {
 uploadUrl: '/api/upload',
 multiple: true,
 allowedExtensions: '.jpg,.jpeg,.png,.gif,.pdf,.docx',
 onValidationError: function(msg, name) { log((name||'file') + ': ' + msg); }
 });
})();

A rule the component enforces

allowedExtensions defaults to an empty string, meaning everything is allowed. Give it a list and files failing it are rejected before any transfer starts, so the user finds out immediately instead of after a wait. It also supplies the file dialog's filter when no accept is set, so the picker and the rule stay in agreement.

Tell the user what happened

onValidationError receives (message, fileName, uploader). Wire it up: a file that silently fails to appear in the queue is indistinguishable from a broken control, and the user's next move is to try the same file again.

An extension is part of a file name, which is to say it is a claim. It stops accidents, not attempts. validateMimeByMagic checks the actual bytes, and the server has to check regardless — it is the only place the user cannot reach.