npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

multipleupload

v1.4.0

Published

Full-featured file upload component for any backend. Pluggable strategies (direct-to-S3 multipart, direct-to-Azure Blob, tus 1.0.0, server-side URL import), chunked uploads with parallel chunks, IndexedDB-backed resume (Golden Retriever equivalent), Web W

Readme

MultipleUpload

A standalone JavaScript file upload component that works with any backend. No runtime dependencies, no framework required.

npm install multipleupload

Transports

Pick one with a single strategy option — the UI, queue, retry and progress model stay identical across all of them.

| strategy | what it does | |---|---| | single | one multipart/form-data POST | | chunked | splits the file, parallel chunks, per-chunk retry, server-side assembly | | s3 | browser uploads parts straight to S3 (or MinIO, R2, B2, Wasabi) via your presigned URLs | | azure | PutBlock + PutBlockList against a blob SAS URL | | gcs | GCS resumable session, Content-Range chunks | | tus | tus 1.0 client (Core + Creation + Termination); interops with tusd | | urlImport | server fetches a pasted URL and streams it through your storage |

With s3, azure and gcs the bytes never touch your app server — it only signs.

Features

  • Resume that survives a crash — every in-flight upload persists to IndexedDB; with persistBlobs: true the File objects too, so a tab crash or reload picks up from the last confirmed chunk
  • Instant upload — opt-in SHA-256 content dedupe; a match completes with zero bytes transferred (the server hashes its own storage; client-claimed hashes are never trusted)
  • Remote sources — Google Drive, Dropbox, OneDrive, Box, Unsplash, and a self-hosted Companion broker that keeps OAuth tokens server-side
  • Image pipeline — resize, compress, watermark, EXIF auto-orient, AVIF/WebP output, and an inline editor with interactive crop, rotate/flip and annotation
  • Folder uploads — webkitdirectory and directory drops preserve each file's relative path, sanitized server-side
  • Every input source — drag-and-drop, full-page drop overlay, clipboard paste, webcam, screen and audio capture
  • Validation — extensions, MIME with magic-byte sniffing, size, image dimensions, duplicates, plus your own rules; re-checked server-side
  • Web Worker hashing — SHA-256/CRC32 off the main thread, CSP-safe fallback
  • 30 built-in locales including RTL, plus dark mode and CSS-variable theming
  • Framework wrappers for React, Vue, Svelte and Angular — or use it plain
  • Node/Express middleware included for single, chunked and scan endpoints
  • ESM, CommonJS and UMD from one engine build, with TypeScript declarations

Quick Start

CDN

<link rel="stylesheet" href="https://unpkg.com/multipleupload/dist/multipleupload.css">
<script src="https://unpkg.com/multipleupload/dist/multipleupload.js"></script>

unpkg serves raw paths and ignores the exports map, so the dist/ segment is required.

Licensed deployments can place a sibling multipleupload.lic next to multipleupload.js. The runtime auto-loads that file by default.

npm

npm install multipleupload

Both module systems are supported from the same engine build — there is only one copy of the runtime in the package, so bundlers never duplicate it.

// ESM (Vite, webpack, Rollup, Next.js, Node ESM)
import { MultipleUpload } from 'multipleupload';
import 'multipleupload/multipleupload.css';

// or a default import
import MultipleUpload from 'multipleupload';
// CommonJS
const MultipleUpload = require('multipleupload');
// destructuring works too
const { MultipleUpload } = require('multipleupload');

TypeScript resolves types under node, node16/nodenext, and bundler module resolution.

Usage

<div id="uploader" class="mu-uploader">
    <button type="button" class="mu-select-btn">Select Files</button>
    <input type="file" class="mu-file-input" multiple style="display:none" />
    <div class="mu-dropzone">
        <div class="mu-dropzone-text">Drag & drop files here</div>
    </div>
    <div class="mu-preview-area"></div>
    <div class="mu-progress" style="display:none">
        <div class="mu-progress-bar"><div class="mu-progress-fill"></div></div>
        <div class="mu-progress-text"></div>
    </div>
    <div class="mu-queue"></div>
</div>

<script>
var uploader = new MultipleUpload('#uploader', {
    uploadUrl: '/api/upload',
    multiple: true,
    maxFileSize: 100 * 1024 * 1024,
    onComplete: function(files) {
        console.log('All uploads complete:', files);
    }
});
</script>

Licensed File Deployment

Licensed customers can replace dist/multipleupload.lic with the multipleupload.lic downloaded from Client Center. multipleupload.js auto-loads a sibling multipleupload.lic, or you can point to a custom location with licenseUrl.

<script src="/assets/multipleupload.js"></script>
<script>
var uploader = new MultipleUpload('#uploader', {
    uploadUrl: '/api/upload',
    licenseUrl: '/licenses/multipleupload.lic'
});
</script>

Framework Wrappers

Each wrapper owns the lifecycle (mount, update, tear-down) and forwards every core event, so you never hand-wire useEffect cleanup.

import { useUploader } from 'multipleupload/react';
import { useUploader } from 'multipleupload/vue';
import { uploader }    from 'multipleupload/svelte';   // Svelte action
import { UploaderService } from 'multipleupload/angular';

The framework packages are peerDependencies — install only the one you use.

Node Middleware

import multipleupload from 'multipleupload/server';

// single + chunked endpoints, relative-path sanitizing, optional instant-upload probe
app.use('/api/upload', multipleupload({ uploadDir: './uploads' }));

multipleupload/server/companion hosts the OAuth broker for the remote sources, and multipleupload/server/adapters provides the storage destinations.

Backend-Agnostic Options

new MultipleUpload('#uploader', {
    headers: function(task) {
        return {
            'Authorization': 'Bearer YOUR_TOKEN',
            'X-Upload-Id': task.id
        };
    },
    withCredentials: true,
    fieldName: 'attachment',
    chunkUrl: '/api/upload/chunk',
    chunkCompleteUrl: '/api/upload/chunk/complete',
    deleteUrl: '/api/files/delete',
    responseParser: function(responseText, task) {
        var data = JSON.parse(responseText);
        return {
            success: true,
            fileGuid: data.id,
            fileName: data.name || task.fileName,
            fileSize: data.size
        };
    },
    transformPipeline: [
        function(file) {
            return file;
        }
    ],
    allowedMimeTypes: 'image/*,application/pdf',
    validateMimeByMagic: true,
    customValidation: function(file, uploader) {
        if (uploader.getTasksByStatus('completed').length > 25) {
            return 'Too many completed files in the queue.';
        }
    }
});

Folder Uploads (Relative Paths)

Folder drops and webkitdirectory picks populate task.relativePath ('photos/2026/beach.jpg', '' for plain files). The path is sent as the relativePath form field (plus an X-Upload-Relative-Path header) on single uploads and inside the chunk-complete body on chunked uploads. The bundled Node middleware sanitizes it (traversal removed) and echoes it on onComplete / response info.relativePath so your app can recreate the tree — always join it under a fixed root.

Instant Upload (Content Dedupe, Opt-In)

// Server (Node middleware): hashes stored uploads with SHA-256 server-side
// and answers GET /exists?hash=&size=. Off by default — the probe reveals
// whether a given content hash exists on this server.
app.use('/api/upload', multipleupload({ uploadDir: './uploads', instantUpload: true }));

// Client: probe before transferring; a confirmed match completes instantly
// with the existing file's id and zero bytes transferred.
new MultipleUpload('#uploader', {
    instantUpload: true,
    onInstantUpload: function(task, result) {
        console.log(task.fileName + ' deduped -> ' + result.fileGuid);
    }
});

Client-claimed hashes are never used for dedupe — the server hashes content itself. Renditions and encrypted uploads never probe.

License

Commercial license. See multipleupload.com for details.