Uppy: A Modular Browser Uploader With a Server-Side Companion
The next open source file uploader for web browsers :dog:
At a glance
- What is it?
- Uppy splits file upload into a browser plugin system and an optional Node service called Companion, which fetches files from Dropbox, Box and Google Drive. The plugin model is genuinely modular; the remote-source path is not, because it requires you to run and maintain a second service.
- Who is it for?
- Adopt Uppy if you need resumable uploads and a customizable browser UI, and you can either run Companion or skip remote sources entirely. Do not adopt it if you want a single npm package with a working dashboard and no server component, because the remote-source plugins will not function without a Companion instance.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The Upload Problem Uppy Actually Targets
A file input and a POST request handle small files. They fall apart on a 2 GB video over a flaky mobile connection, on a form where the user needs to attach a file from their Dropbox rather than their laptop, and on any interface where you want a progress bar, a preview and a metadata editor without writing all three. Uppy is aimed at teams that have hit at least one of those walls and would rather assemble existing plugins than build the upload layer themselves.
The README frames the pitch as letting you "worry about more important problems than building a file uploader." The audience implied by the repository layout is frontend engineers working in JavaScript or TypeScript, and the docs list first-class integrations for plain JS/HTML, React, Svelte, Vue and Angular. The project is maintained by Transloadit, a commercial file processing API, and the README states that Uppy works with Transloadit for encoding and delivery and also works without it if you run your own pipeline.
Plugins, Events and the Companion Split
The core object is an Uppy instance, and every capability is attached with .use(). The README example chains Dashboard, RemoteSources, Webcam, ImageEditor and Tus onto one instance, then subscribes to a complete event that receives a result object. That is the whole data flow at the top level: plugins register themselves, the core tracks files and their upload state, and lifecycle events fire as files move through it.
The interesting architectural split is between local and remote sources. Local files come from a drag-and-drop area, a file input, or the webcam. Remote files come from Dropbox, Box, Google Drive or a remote URL, and those are handled by @uppy/companion, a separate server process. The README says Companion lets you bypass the user's device where possible, "syncing between servers directly." That phrasing matters: the file does not travel through the browser at all for those providers. Companion holds the OAuth credentials, talks to the provider's API, and streams the file to your storage endpoint. Your browser code never sees the bytes.
Resumable uploads go through tus, an open standard the README describes as letting large uploads "survive network hiccups." That is a protocol-level guarantee, not a retry loop bolted onto fetch. There is also Golden Retriever, an optional plugin for recovering files after a browser crash. The README lists it as optional, which is the right framing: it is state persistence, and it has its own storage and expiry questions.
Installing It and Wiring the First Upload
The npm path is three packages for a minimal setup:
npm install @uppy/core @uppy/dashboard @uppy/tus
The README also requires the stylesheet, uppy.min.css, either linked in the page head or imported in JS if your bundler supports it. The CDN alternative loads a pre-built bundle that attaches Uppy to window.Uppy, and the README explicitly warns against it for production because the bundle "consists of most Uppy plugins" and users download all of them. That is a real cost, not a stylistic preference. If you are shipping to mobile users on metered connections, the modular npm path is the one to take.
The README's own example gives the concrete option shapes. Dashboard takes a trigger selector or a target element. RemoteSources takes a companionUrl, and the example points it at https://companion.uppy.io, which is a hosted demo instance rather than something you should route production traffic through. Tus takes an endpoint, shown as https://tusd.tusdemo.net/files/, which is likewise a demo server. Both of those URLs are placeholders for your own infrastructure, and swapping them is the first thing to do after the example runs.
For framework work, the README describes three UI approaches: pre-composed components like Dashboard, headless components you style and compose yourself, and hooks that attach Uppy's logic to your own components. The README is honest that the pre-composed route limits customization. Choosing between the three is the main design decision on the frontend, and it is hard to reverse later without rewriting your upload UI.
Where Uppy Stops Being the Right Tool
Companion is the sharpest constraint. The README documents it as a separate instance you set up and run, and the remote-source plugins depend on it. If your deployment model is a static site with no server component, Dropbox, Box and Google Drive imports are unavailable to you unless you accept the operational weight of running Companion somewhere. The docs page for it is listed under documentation, so setup details exist, but the repository material here does not cover them, and I cannot tell you from this material what Companion's resource footprint or scaling behaviour looks like.
There is a second boundary worth stating plainly. Uppy is a client-side uploader with an optional fetch service. It is not a storage backend. The Tus plugin needs a tus-compatible server, and the README's example endpoint is someone else's demo. If you do not already have tusd, or a service that speaks tus, you are adding that to your stack. The README mentions running your own Apache, Nginx, Node or FFmpeg pipeline as the alternative to Transloadit, which is accurate but also a description of a project, not a weekend task.
The plugin granularity cuts both ways. Installing only what you use keeps the bundle small, but it also means the version matrix is yours to manage. The release list shows @uppy/core, the uppy meta-package and @uppy/companion versioned independently, with Companion on a 7.x line while core is on 6.x. Nothing in this material says those must move together, and I would not assume they do.
Uppy Versus a Plain tus Client
The closest comparison is not another uploader widget. It is using tus-js-client directly, or a similar resumable-upload library, and building the interface yourself. The difference in approach is where the abstraction sits. A tus client handles one thing: the resumable transfer protocol. You write the file picker, the progress display, the preview, the metadata form and the retry messaging.
Uppy bundles all of that as plugins on top of the same tus foundation, which is why the README can advertise Dashboard, Webcam, ImageEditor and i18n as features rather than as separate dependencies. The trade is control and bundle size against assembly time. If your upload UI is a single button and a progress bar, a tus client plus your own markup is less machinery. If you need remote sources, a metadata editor, camera capture and a recovery path after a crash, rebuilding that on a bare tus client means reimplementing most of Uppy.
The other axis is the Companion service. A plain tus client has no server-side fetch component at all, because it never claims to import from Dropbox. Uppy's remote-source feature is the reason Companion exists, and it is the reason Uppy cannot be described as a purely client-side library.
Licence, Maintenance and What a Major Bump Costs You
Uppy is MIT licensed, and the README describes it as "free for the world, forever." MIT is permissive: you can use it commercially, modify it and redistribute it, provided the copyright notice and licence text are preserved. That is the general shape of the licence, not legal advice, and the Companion service being MIT too does not change the terms of whatever storage backend you point it at.
The maintenance picture visible here is active. The last push to main is dated 2026-09-10, and releases for uppy@6.0.1, @uppy/core@6.0.1 and @uppy/companion@7.0.2 all landed in the first week of September 2026. The 6.0 and 7.0 majors are recent, which means the migration guides matter more than usual if you are on an earlier line. This material does not include the changelogs, so I cannot tell you what broke between majors. Read them before upgrading.
Upgrade cost is concentrated in two places. The plugin options you pass to .use() are the API surface most likely to shift across majors, and the Companion deployment is the piece that needs a redeploy on its own schedule. Because Companion has its own version line, a Companion upgrade and an @uppy/core upgrade are two separate events in your release process, and treating them as one is how you end up debugging a version mismatch at the wrong layer.
Who Should Take It and What to Check First
The fit is a web application with a real upload surface: large files, unreliable networks, users who want to pull from cloud storage, and a frontend team that has already decided the built-in browser file input is not enough. Uppy gives that team a plugin system, a tus path, and a documented route from pre-composed components down to hooks when the default UI stops fitting.
The misfit is a project that wants one dependency and no server. If you cannot or will not run Companion, cross off the remote-source plugins before you evaluate anything else, because they will not work. The same applies if you have no tus-capable endpoint and no appetite to add one. In that case a smaller library, or the platform's own file input, is the honest choice.
What to verify before adopting: confirm which tus server you will point the Tus plugin at, since the README's endpoint is a public demo; work out how Companion gets deployed and where its OAuth credentials for each provider live; and pin your @uppy/core version deliberately, because the plugin packages and Companion version independently and the release list shows they are not always on the same major. The README's own CDN warning is the fourth check: if you were planning to load uppy.min.mjs from the CDN, measure the bundle first, because it ships most plugins whether you use them or not.
Editorial conclusion
Adopt Uppy if you need resumable uploads and a customizable browser UI, and you can either run Companion or skip remote sources entirely. Do not adopt it if you want a single npm package with a working dashboard and no server component, because the remote-source plugins will not function without a Companion instance. Before committing, verify three things: whether your target browsers support the tus protocol path you plan to use, what Companion's OAuth setup requires for each provider you enable, and how the plugin versions you install line up with the @uppy/core major you pin.
Community notes