Uppy: A Modular File Uploader for the Browser
The next open source file uploader for web browsers :dog:
At a glance
- What is it?
- Uppy is a TypeScript file uploader built from composable plugins, with a Companion server for remote sources and tus for resumable transfers. It rewards teams that need control over the upload UI and punishes anyone who wants a drop-in widget with no server to run.
- Who is it for?
- Adopt Uppy if you are building a web app that needs uploads with previews, restrictions and resumable transfers, and you are willing to run or host Companion when you want cloud sources. Do not adopt it if you want a single script tag with no build step, or if you cannot operate a Node service for remote providers.
- 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 9 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Uppy solves, and who it is actually for
Building a file uploader from scratch means writing drag and drop, progress reporting, retries, file type and size validation, image previews, and some way to survive a dropped connection on a large file. Uppy packages those concerns as separate plugins, so a team can take the parts it needs. The repository describes it as a modular JavaScript file uploader that integrates with any application, and the package layout backs that up: packages/@uppy/* holds individual plugins, while packages/uppy is the bundled entry point.
The audience is front-end engineers working in a framework that already has a build step. The README documents first-class support for plain JS/HTML, React, Svelte, Vue and Angular. For the supported frameworks except Angular, it offers three integration levels: pre-composed components such as Dashboard, headless components you style yourself, and hooks that attach Uppy logic to components you already own. That third option is the reason to pick Uppy over a widget: the upload state machine is separable from the markup.
It is not aimed at someone who wants a hosted upload endpoint. Uppy runs in the browser and sends files wherever you point it. The README notes it works well with Transloadit for encoding and delivery, and works well without one if you would rather run your own Apache, Nginx, Node or FFmpeg pipeline. Transloadit maintains the project, which is worth knowing when you evaluate the commercial relationship between the uploader and the processing service.
How the plugin architecture and Companion fit together
An Uppy instance is created from @uppy/core and then extended with .use(). Each plugin registers its own state, UI and upload logic. The README's example chains Dashboard, RemoteSources, Webcam, ImageEditor and Tus onto one instance, then subscribes to the complete event. That is the whole data flow: files enter through a source plugin, land in the core file list, get previewed or edited by UI plugins, and leave through a destination plugin such as Tus or XHR.
Because plugins are separate packages, the dependency footprint depends on which ones you import. The README explicitly warns against the pre-built CDN bundle for production, since it contains most Uppy plugins and your users would download all of them. The alternative is per-plugin imports, which is what the npm install command in the README sets up.
Remote sources are the part that needs a server. Fetching from Dropbox, Box, Google Drive or a remote URL goes through @uppy/companion, a Node service. The repository ships a Dockerfile for it, and the .env.example file lists the configuration surface: COMPANION_DOMAIN, COMPANION_PROTOCOL, COMPANION_PORT (3020 by default), COMPANION_SECRET, COMPANION_PREAUTH_SECRET, and per-provider keys such as COMPANION_DROPBOX_KEY, COMPANION_BOX_KEY and COMPANION_GOOGLE_KEY. The example file also sets COMPANION_ALLOW_LOCAL_URLS=true with a note that enabling it in production is a security risk. That warning is the clearest signal in the repository that Companion is a real network service with a real attack surface, not a static asset.
Installing Uppy and getting a working Dashboard on the page
The README's installation section gives a single npm command that pulls core, Dashboard and the tus destination. Run it in your project root.
npm install @uppy/core @uppy/dashboard @uppy/tusThe README then says to add the stylesheet, either into the page head or through your bundler. The URL is versioned, so it matches the release you installed.
<link
href="https://releases.transloadit.com/uppy/v6.0.1/uppy.min.css"
rel="stylesheet"
/>With the CSS in place, the README's example creates an instance, attaches Dashboard to a trigger element, and points Tus at an endpoint. The complete handler receives the upload result.
import Uppy from '@uppy/core'
import Dashboard from '@uppy/dashboard'
import Tus from '@uppy/tus'
const uppy = new Uppy()
.use(Dashboard, { trigger: '#select-files' })
.use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' })
.on('complete', (result) => {
console.log('Upload result:', result)
})What you should see is a Dashboard panel opening when the trigger element is clicked, with a drop area and progress bars. The endpoint in that snippet is tusd's public demo server, which the README uses for illustration. Sending real user files to a third-party demo endpoint is not something to do outside a test. Swap in your own tus endpoint before this leaves local development. If your backend speaks plain multipart instead, the repository's examples directory contains xhr-node, xhr-php and xhr-python variants that show the alternative destination plugin.
Where Uppy gets awkward: Companion, bundles and the server you now own
The largest limitation is the split between client and server. Everything in @uppy/core and the UI plugins runs in the browser, but the moment you want Dropbox, Box, Google Drive or remote URL imports, you need @uppy/companion deployed and reachable, with OAuth credentials for each provider. The .env.example file shows how many secrets that involves. A team that only needs local file selection can skip Companion entirely, and should, because running it adds a Node service to your infrastructure for no benefit.
Companion is also the component with the sharpest deployment constraints. Its Dockerfile builds on node:22.22.1-alpine, installs build tooling in a virtual package, builds the @uppy/companion workspace, then prunes to production dependencies. It exposes port 3020 and runs node /app/dist/bin/companion.js. That is a straightforward image, but it means you are operating a service behind a domain that must match COMPANION_DOMAIN and COMPANION_PROTOCOL, or the OAuth redirects will not line up.
The second limitation is bundle discipline. The README's own warning about the pre-built bundle is easy to ignore during prototyping and expensive to fix later, because the global window.Uppy object encourages string-based plugin registration that does not tree-shake. If you start with the CDN bundle, migrating to per-plugin imports later means rewriting initialization code.
Uppy is the wrong tool when the upload is incidental. If a form needs one file field and a progress bar, a few lines of fetch with FormData will be smaller, faster to ship and easier to reason about than a plugin graph.
Uppy against Dropzone and plain FormData
Dropzone is the closest widely used alternative in the same category: a JavaScript library that turns an element into a drag and drop upload zone. The difference in approach is compositional versus configured. Dropzone is configured through options on one object and renders its own previews; Uppy is assembled from plugins, and the UI can be replaced entirely with headless components or hooks. If you want to keep your own design system and only borrow upload logic, Uppy's hooks path exists for that and Dropzone's does not.
The other real alternative is no library at all. A fetch call with a FormData body handles the simple case, and the browser gives you upload progress through XMLHttpRequest if you need it. What you give up is resumability. Uppy's tus plugin implements the open tus protocol, which the README describes as the reason large uploads survive network hiccups. Reimplementing that means implementing the tus protocol or something like it, which is a much bigger project than it sounds.
Between those poles, the honest framing is that Uppy's cost is architectural complexity and an optional server, and its benefit is resumable transfers plus remote sources plus a UI layer you can replace. Pick it when you need at least two of those three.
Maintenance, licensing and upgrade cost
The repository is not archived, and the last push was on 2026-09-10. Releases are frequent and independently versioned: [email protected], @uppy/[email protected] and @uppy/[email protected] all landed on 2026-09-07. Note that core and Companion carry different major versions, so a Companion upgrade is not implied by a core upgrade. Read the changelog for each package before bumping.
The monorepo is a Yarn workspaces setup driven by turbo, with biome for linting and formatting. If you contribute rather than consume, the scripts are build, test, test:e2e and typecheck, all filtered through turbo. If you only consume the published packages, none of that tooling reaches your project.
Licensing is MIT, per the LICENSE file and the package.json license field. That permits commercial use and modification, and it is compatible with closed-source applications. It also means there is no warranty and no support obligation from the maintainers. The Companion server is part of the same MIT-licensed repository, so running it does not create a commercial relationship with Transloadit. If you use the Transloadit service for encoding, that is a separate commercial arrangement with its own terms, and this article does not evaluate it. This is a description of the licence text, not legal advice; have counsel review anything that matters.
Editorial conclusion
Adopt Uppy if you are building a web app that needs uploads with previews, restrictions and resumable transfers, and you are willing to run or host Companion when you want cloud sources. Do not adopt it if you want a single script tag with no build step, or if you cannot operate a Node service for remote providers. Before committing, verify two things: that your bundler picks up the per-plugin packages rather than the pre-built bundle, and that your tus endpoint or XHR endpoint accepts the requests your chosen plugin sends.
Frequently asked questions
What is a file uploader used for?
In Uppy's case, it moves files from a user's device or a remote source into your application. The README lists fetching from local disk, remote URLs, Google Drive, Dropbox and Box, previewing and editing metadata, then uploading to a final destination with optional processing or encoding.
What is the best file uploader?
The repository does not rank uploaders. Uppy's own claim is that it is modular and plugin-based, with resumable uploads via the open tus standard and first-class integrations for plain JS/HTML, React, Svelte, Vue and Angular. Whether that fits depends on whether you need resumable transfers or remote sources.
How do I install Uppy?
The README gives npm install @uppy/core @uppy/dashboard @uppy/tus, then adding uppy.min.css either to the page head or through your bundler. A pre-built bundle from Transloadit's CDN is also offered, but the README warns it is not recommended for production because it contains most plugins.
Do I need to run Uppy Companion to use Uppy?
No. Core and the UI plugins run in the browser. Companion is only needed for Dropbox, Box, Google Drive and remote URL sources, and the README documents it as a separate server you set up and run.
What does the Uppy Dashboard plugin do?
The README describes Dashboard as the universal UI with previews, progress bars and a metadata editor, and notes it is required for most UI plugins such as Webcam. It can be attached to a target element or opened from a trigger.
Is Uppy free to use?
The repository is MIT licensed, and the README states it is free for the world, forever. MIT permits commercial and closed-source use with no warranty or support obligation. Using Transloadit's encoding service is a separate arrangement.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/transloadit-uppy)