Skillshub uploadthing

UploadThing

install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/TerminalSkills/skills/uploadthing" ~/.claude/skills/comeonoliver-skillshub-uploadthing && rm -rf "$T"
manifest: skills/TerminalSkills/skills/uploadthing/SKILL.md
source 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).