Skills uploadthing
install
source · Clone the upstream repo
git clone https://github.com/TerminalSkills/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/TerminalSkills/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/uploadthing" ~/.claude/skills/terminalskills-skills-uploadthing && rm -rf "$T"
manifest:
skills/uploadthing/SKILL.mdsource content
UploadThing
Overview
UploadThing is a file upload service for TypeScript apps. Define upload routes on the server (with auth, file type, and size validation), get pre-built React components for the frontend. Files go to S3-compatible storage. No infrastructure to manage — just define what's allowed and upload.
Instructions
Step 1: Setup
npm install uploadthing @uploadthing/react
Step 2: Server Routes
// server/uploadthing.ts — Define upload routes import { createUploadthing, type FileRouter } from 'uploadthing/server' import { getSession } from '@/lib/auth' const f = createUploadthing() export const uploadRouter = { // Avatar upload: max 2MB image, authenticated users only avatarUploader: f({ image: { maxFileSize: '2MB', maxFileCount: 1 } }) .middleware(async ({ req }) => { const session = await getSession(req) if (!session) throw new Error('Not authenticated') return { userId: session.user.id } }) .onUploadComplete(async ({ metadata, file }) => { console.log(`Avatar uploaded for user ${metadata.userId}: ${file.url}`) await db.user.update({ where: { id: metadata.userId }, data: { avatarUrl: file.url }, }) return { url: file.url } }), // Document upload: max 10MB, multiple files documentUploader: f({ pdf: { maxFileSize: '10MB', maxFileCount: 5 }, 'application/msword': { maxFileSize: '10MB', maxFileCount: 5 }, }) .middleware(async ({ req }) => { const session = await getSession(req) if (!session) throw new Error('Not authenticated') return { userId: session.user.id } }) .onUploadComplete(async ({ metadata, file }) => { await db.document.create({ data: { name: file.name, url: file.url, size: file.size, userId: metadata.userId, }, }) }), } satisfies FileRouter export type OurFileRouter = typeof uploadRouter
Step 3: React Components
// components/AvatarUpload.tsx — Pre-built upload button import { UploadButton, UploadDropzone } from '@uploadthing/react' import type { OurFileRouter } from '@/server/uploadthing' // Simple button export function AvatarUpload() { return ( <UploadButton<OurFileRouter, 'avatarUploader'> endpoint="avatarUploader" onClientUploadComplete={(res) => { console.log('Uploaded:', res[0].url) }} onUploadError={(error) => { console.error('Upload failed:', error.message) }} /> ) } // Drag and drop zone export function DocumentUpload() { return ( <UploadDropzone<OurFileRouter, 'documentUploader'> endpoint="documentUploader" onClientUploadComplete={(res) => { console.log(`${res.length} files uploaded`) }} /> ) }
Guidelines
- Free tier: 2GB storage, 2GB transfer/month — enough for MVPs.
- UploadThing handles presigned URLs, multipart upload, and CDN delivery.
- Middleware runs on every upload — use for auth, rate limiting, and validation.
- For self-hosted alternative, use S3 + presigned URLs directly (more work, no vendor lock-in).