Uploadthing
Type-safe file uploads for Next.js and full-stack TypeScript apps. Handles S3 under the hood, works in serverless environments, and provides a strongly-typed router with built-in auth callbacks.
Why Uploadthing?
Next.js app that needs file uploads without managing S3 yourself
Type-safe upload routes with per-route validation and auth
Serverless or edge deployment where direct S3 multipart is tricky
Signal Breakdown
What drives the Trust Score
Download Trend
Last 12 months
Tradeoffs & Caveats
Know before you commitNeed image transformations on upload — use Cloudinary instead
Non-Next.js stack — support outside Next.js/React is limited
Need video processing or CDN streaming — too limited for media pipelines
Pricing
Free tier & paid plans
2GB storage · 2GB bandwidth/mo
From $10/mo (Pro)
Storage and bandwidth-based pricing
Alternative Tools
Other options worth considering
Often Used Together
Complementary tools that pair well with Uploadthing
Learning Resources
Docs, videos, tutorials, and courses
Get Started
Repository and installation options
View on GitHub
github.com/pingdotgg/uploadthing
npm install uploadthing @uploadthing/reactQuick Start
Copy and adapt to get going fast
// app/api/uploadthing/core.ts
import { createUploadthing, type FileRouter } from 'uploadthing/next';
const f = createUploadthing();
export const ourFileRouter = {
profilePicture: f({ image: { maxFileSize: '4MB', maxFileCount: 1 } })
.middleware(async ({ req }) => {
const user = await currentUser(); // your auth
if (!user) throw new Error('Unauthorized');
return { userId: user.id };
})
.onUploadComplete(async ({ metadata, file }) => {
await db.user.update({ where: { id: metadata.userId }, data: { avatarUrl: file.url } });
}),
} satisfies FileRouter;
export type OurFileRouter = typeof ourFileRouter;Code Examples
Common usage patterns
Profile picture upload
Upload and save user avatar with auth check
// components/UploadButton.tsx
'use client';
import { UploadButton } from '@uploadthing/react';
import type { OurFileRouter } from '../api/uploadthing/core';
export function ProfilePictureUpload() {
return (
<UploadButton<OurFileRouter>
endpoint="profilePicture"
onClientUploadComplete={(res) => {
console.log('Files:', res);
alert('Upload complete!');
}}
onUploadError={(error: Error) => {
alert(`Error: ${error.message}`);
}}
/>
);
}Multi-file document upload
Allow uploading multiple PDFs with size validation
// core.ts — add a document uploader
documentUploader: f({
pdf: { maxFileSize: '16MB', maxFileCount: 5 },
'application/msword': { maxFileSize: '8MB' },
})
.middleware(async ({ req }) => {
const session = await getServerSession(req);
if (!session?.user) throw new Error('Must be logged in');
return { userId: session.user.id, orgId: session.user.orgId };
})
.onUploadComplete(async ({ metadata, file }) => {
await db.document.create({
data: { url: file.url, name: file.name, orgId: metadata.orgId, uploadedBy: metadata.userId },
});
}),Community Notes
Real experiences from developers who've used this tool