Skip to main content

Uppy AWS S3 plugin

Uppy is a popular open-source file uploader for the browser. Its @uppy/aws-s3 plugin uploads directly to any S3-compatible service via pre-signed URLs.

Install

npm install @uppy/core @uppy/dashboard @uppy/aws-s3

Backend — issue pre-signed URLs

Uppy calls a backend endpoint to get a pre-signed URL for each upload. Here's a minimal Express handler:

server/index.ts
import express from 'express';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const s3 = new S3Client({
endpoint: 'https://s3.filebase.io',
region: 'auto',
credentials: {
accessKeyId: process.env.FILEBASE_KEY!,
secretAccessKey: process.env.FILEBASE_SECRET!,
},
});

const app = express();
app.use(express.json());

app.post('/uppy/sign', async (req, res) => {
const { filename, contentType } = req.body;
const key = `uploads/${Date.now()}-${filename}`;

const url = await getSignedUrl(
s3,
new PutObjectCommand({
Bucket: 'my-uppy-bucket',
Key: key,
ContentType: contentType,
}),
{ expiresIn: 600 },
);

res.json({ method: 'PUT', url });
});

app.listen(3000);

For multipart uploads (files > 100 MB), Uppy uses a multi-step protocol — see the Uppy AWS S3 multipart docs. The pre-signing logic is similar but signs CreateMultipartUpload, each UploadPart, and CompleteMultipartUpload.

Frontend — Uppy dashboard

src/uploader.ts
import Uppy from '@uppy/core';
import Dashboard from '@uppy/dashboard';
import AwsS3 from '@uppy/aws-s3';

import '@uppy/core/dist/style.css';
import '@uppy/dashboard/dist/style.css';

const uppy = new Uppy({
restrictions: {
maxFileSize: 5 * 1024 * 1024 * 1024, // 5 GB
allowedFileTypes: ['image/*', 'video/*'],
},
})
.use(Dashboard, { target: '#uploader', inline: true })
.use(AwsS3, {
getUploadParameters(file) {
return fetch('/uppy/sign', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
filename: file.name,
contentType: file.type,
}),
}).then((r) => r.json());
},
});

uppy.on('upload-success', (file, response) => {
console.log('Uploaded:', response.uploadURL);
});
<div id="uploader"></div>

CORS

Browser uploads require CORS on your bucket. See configure CORS for an SPA for the policy. A minimum CORS rule for Uppy:

{
"CORSRules": [
{
"AllowedMethods": ["PUT", "POST", "GET", "HEAD"],
"AllowedOrigins": ["https://your-app.com"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"]
}
]
}

What's next