Uppy splits the uploader into plugins, and marks the ones that need a server
The next open source file uploader for web browsers :dog:
At a glance
- What is it?
- A MIT licensed, TypeScript file uploader where Dashboard holds the interface, Tus, XHR Upload and AWS S3 hold the destinations, and a ⓒ beside a source plugin means you also have to run Companion. Three UI integration styles for every supported framework except Angular.
- Who is it for?
- Uppy suits a team that wants a uploader with previews, metadata editing and resumable transfers without writing drag and drop, retried chunk requests and progress UI by hand, and it does not suit a static site with no server, because the cloud drive plugins are inert without Companion. Before you start, decide which sources you actually need, since every ⓒ plugin turns a client-side widget into a service you have to deploy and secure.
- 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 1 day 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 November 13, 2026, and from our analysis. They are not legal advice.
Star history
- transloadit/uppy31,068 ★ · +98
| Repository | 2026-09-13 | 2026-11-13 |
|---|---|---|
| transloadit/uppy | 31k | 31.1k |
98 Stars in the last 61 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The Dashboard plugin is what most other UI plugins need
Uppy is assembled by chaining plugins onto one instance, and the chain reads like the architecture:
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)
})`@uppy/core` knows nothing about the interface. `Dashboard` supplies it, with previews, progress bars and a metadata editor, and the documentation is blunt that it is required for most UI plugins such as Webcam. `Tus` is the destination. Three packages, three jobs, and the only event you handle at the start is `complete`.
That split is the reason the library can sit behind a plain button, a drag area or a full dashboard without rewriting anything. Everything a user sees comes from a plugin you chose to switch on.
Three ways to build the interface, and Angular gets only one
First-class support is claimed for plain JS/HTML, React, Svelte, Vue and Angular. For every framework except Angular, the project offers three routes, and the choice is about how much of the look you keep.
Pre-composed plug-and-play components are mainly `<Dashboard />`. Fastest to ship, and the stated downside is that you cannot customise the UI.
Headless components are smaller pieces you override the styles of or compose with your own components. You keep Uppy's behaviour and write the markup.
Hooks attach the library's logic to your own components with no restrictions, which is the route for a tailor-made interface.
Angular is the exception: the README lists it as first-class but says the three routes apply to the supported frameworks except Angular, and points to a separate Angular docs page.
The ⓒ sources all need a Companion server, and its dev flags say so
The source plugins come in two flavours, and the difference is marked in the list with a ⓒ. Google Drive, Dropbox, Box, Facebook, OneDrive and Import From URL all carry it, and the mark means `@uppy/companion`, a server-side component, is required before the plugin can work. Drag & Drop, File Input and Webcam do not.
The reason is architectural, and it is the interesting part of the design. Companion exists so Uppy can bypass the user's device where possible and sync between servers directly, instead of pulling a file from Google Drive into a browser and pushing it on to your storage. The browser stops being the relay.
That server is yours to run and yours to configure, and the sample environment file is explicit about which knobs are development-only. `COMPANION_ALLOW_LOCAL_URLS=true` is marked as a security risk if enabled in production. Alongside it sit `COMPANION_PORT=3020`, `COMPANION_DOMAIN=localhost:3020`, `COMPANION_SECRET`, `COMPANION_PREAUTH_SECRET`, `COMPANION_CLIENT_ORIGINS=true`, and switches for the URL and Google Picker endpoints.
Tus, XHR Upload and AWS S3 cover three different backends
Destinations are where Uppy stops being a picker and starts being infrastructure, and the three options differ in what they assume about your server.
`Tus` speaks the open tus standard and gives resumable uploads, which is what makes a large file survive a network hiccup: the transfer picks up where it stopped instead of starting again. The example wires it to a public tusd instance, `https://tusd.tusdemo.net/files/`.
`XHR Upload` is the plain option, described as regular uploads for whatever backend you already run, and the documentation names Apache and Nginx as the cases it suits.
`AWS S3` is described as plain upload to AWS S3 or compatible services.
The choice is really about who terminates the upload. Tus needs a tus-compatible endpoint, XHR needs an endpoint that accepts a POST and moves the file, and S3 needs credentials somewhere. Adding file processing is a separate axis again, where the Transloadit plugin creates Assemblies that handle encoding, conversion and delivery, or you run your own Apache, Nginx, Node or FFmpeg pipeline.
The Companion S3 variables can break in a minor release
The sample environment file carries a warning that deserves to be read before you rely on it: the S3 provider's options, those variables and its `/s3/*` endpoints will change incompatibly, and the note says that this applies to minor releases too.
That is unusual enough to plan around. A minor version bump is normally where nothing breaks, so anything you build on those endpoints should be pinned and tested rather than tracked.
The variables themselves are ordinary and worth knowing in advance. `COMPANION_AWS_KEY` and `COMPANION_AWS_SECRET` hold the credentials, with a secret file variant that overrides a directly set secret. `COMPANION_AWS_BUCKET`, `COMPANION_AWS_REGION` and `COMPANION_AWS_PREFIX` place the object. `COMPANION_AWS_FORCE_PATH_STYLE` is documented as useful for localstack support, `COMPANION_AWS_EXPIRES` sets the `X-Amz-Expires` query parameter on presigned URLs with a default of 800 seconds, `COMPANION_AWS_USE_ACCELERATE_ENDPOINT` is off by default, and `COMPANION_AWS_SSE` takes AES256, aws:fsx, aws:kms or aws:kms:dsse with an optional `COMPANION_AWS_SSE_KMS_KEY_ID`.
npm install @uppy/core @uppy/dashboard @uppy/tus, plus one stylesheet
The documented install is three packages, and the reason it is three rather than one is the plugin model again:
npm install @uppy/core @uppy/dashboard @uppy/tusThen you add `uppy.min.css`, either into your HTML page's `<head>` or through JS if your bundler supports importing it. A file uploader that renders its own interface has to ship the styles for that interface, and forgetting this line is the usual reason a Dashboard appears unstyled.
The cloud drive plugins install the same way when you need them, and each one is a separate `@uppy/` package rather than an option inside Dashboard. Companion is the exception: it is not a browser package at all, and the repository builds it as its own service image.
Uppy is developed by Transloadit, and the licence is MIT, so nothing about shipping it in a commercial product needs arranging first.
The CDN bundle attaches to window.Uppy and ships every plugin
There is a second install route, and the project tells you not to use it in production. Transloadit's Smart CDN serves a pre-built bundle, and in that case `Uppy` attaches itself to the global `window.Uppy` object instead of being imported.
The stated reason is payload. The bundle contains most Uppy plugins, so every visitor downloads all of them when you use a few.
The mechanics are ordinary ES modules from a pinned version:
import {
Uppy,
Dashboard,
Tus,
} from 'https://releases.transloadit.com/uppy/v6.0.4/uppy.min.mjs'The version in that path matches the release tag, and the same version supplies `uppy.min.css`, so the two files cannot drift apart if you pin them together.
One more capability sits outside the plugin list. Golden Retriever is described as optional file recovery after a browser crash, which is the case where a user has spent ten minutes on a video and the tab dies.
A yarn and turbo monorepo, with 6.0.4 cut from an older commit
The repository is a workspace rather than a project. `yarn.lock` and `.yarn/` sit at the root beside `turbo.json` and `biome.json`, and the workspaces cover `packages/@uppy/*`, `packages/uppy`, `examples/*` and `private/*`. The root package itself is private and versioned 0.0.0, because nothing installs it.
Examples are part of that layout rather than a separate site, with directories for angular, nextjs, react, reactrouter, sveltekit, vue, cdn, xhr-bundle, xhr-node, xhr-php, xhr-python, aws-companion, aws-nodejs, aws-php, transloadit, companion and companion-custom-provider. The test script runs turbo across the packages with `--concurrency=1` and `--browser.headless`, so the browser suites are serial on purpose. Releases go through `.changeset/` and land in `CHANGELOG.md`.
On timing, the last push is dated 2026-10-04, and the most recent releases listed are @uppy/[email protected], @uppy/[email protected] and @uppy/[email protected], all on 2026-10-16.
Editorial conclusion
Uppy suits a team that wants a uploader with previews, metadata editing and resumable transfers without writing drag and drop, retried chunk requests and progress UI by hand, and it does not suit a static site with no server, because the cloud drive plugins are inert without Companion. Before you start, decide which sources you actually need, since every ⓒ plugin turns a client-side widget into a service you have to deploy and secure.
Frequently asked questions
What is Uppy used for?
Uppy is a modular JavaScript file uploader for the browser. It fetches files from local disk, remote URLs, Google Drive, Dropbox, Box or a camera, previews them and lets you edit their metadata, then uploads to a destination with optional processing or encoding. It is MIT licensed and developed by Transloadit.
How do I install Uppy in a JavaScript project?
Run `npm install @uppy/core @uppy/dashboard @uppy/tus` and then add `uppy.min.css` to your page's `<head>` or import it through your bundler. Alternatively you can load a pre-built bundle from Transloadit's CDN, which attaches `Uppy` to the global `window.Uppy` object, though the project does not recommend that for production because it contains most plugins.
Do Uppy's Google Drive, Dropbox and Box plugins need a server?
Yes. Those source plugins are marked with a ⓒ in the documentation, and the mark means `@uppy/companion`, a server-side component, is needed for them to work. Companion lets Uppy bypass the user's device where possible and sync between servers directly. Drag & Drop, File Input and Webcam need no companion.
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)Star history chart for your README
[](https://hysenlabs.com/projects/transloadit-uppy)Was this useful?