Skip to content
Back to skills

Uploadthing

ASecurity

> Type-safe file uploads for Next.js/React — no S3 config, built-in validation, instant CDN.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 26, 2026
developmenttypescriptreactnextjsapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 26, 2026

npx -y skills add nhan03bit/ultrathink --skill uploadthing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Uploadthing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Uploadthing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/nhan03bit-uploadthing/badge)](https://www.skillsdirectory.com/skills/nhan03bit-uploadthing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# UploadThing

> Type-safe file uploads for Next.js/React — no S3 config, built-in validation, instant CDN.

## When to Use
- Adding file uploads to Next.js or React apps
- Need type-safe upload routes with auth middleware
- Want pre-built or custom upload UI components
- File validation (size, type) without manual wiring

## Core Patterns

### File Router (Server)
```typescript
import { createUploadthing, type FileRouter } from "uploadthing/next";

const f = createUploadthing();

export const uploadRouter = {
  avatar: f({ image: { maxFileSize: "4MB", maxFileCount: 1 } })
    .middleware(async ({ req }) => {
      const user = await auth(req);
      if (!user) throw new UploadThingError("Unauthorized");
      return { userId: user.id }; // Passed to onUploadComplete
    })
    .onUploadComplete(async ({ metadata, file }) => {
      await db.user.update({ where: { id: metadata.userId }, data: { avatar: file.url } });
      return { url: file.url };
    }),

  documents: f({ pdf: { maxFileSize: "16MB" }, image: { maxFileSize: "8MB" } })
    .middleware(async () => ({}))
    .onUploadComplete(({ file }) => ({ url: file.url })),
} satisfies FileRouter;

export type OurFileRouter = typeof uploadRouter;
```

### Route Handler (Next.js App Router)
```typescript
// app/api/uploadthing/route.ts
import { createRouteHandler } from "uploadthing/next";
import { uploadRouter } from "@/server/uploadthing";

export const { GET, POST } = createRouteHandler({ router: uploadRouter });
```

### Client Components
```typescript
import { generateReactHelpers, generateUploadButton, generateUploadDropzone } from "@uploadthing/react";
import type { OurFileRouter } from "@/server/uploadthing";

export const UploadButton = generateUploadButton<OurFileRouter>();
export const UploadDropzone = generateUploadDropzone<OurFileRouter>();
export const { useUploadThing } = generateReactHelpers<OurFileRouter>();

// Pre-built: <UploadButton endpoint="avatar" onClientUploadComplete={(res) => setUrl(res[0].url)} />
// Custom UI with hook:
const { startUpload, isUploading } = useUploadThing("avatar", {
  onClientUploadComplete: (res) => setUrl(res[0].url),
  onUploadError: (err) => toast.error(err.message),
});
// Trigger: await startUpload(files)
```

### Key Features
- **Validators**: `.image()`, `.pdf()`, `.video()`, `.audio()`, `.blob()` with `maxFileSize`/`maxFileCount`
- **Middleware**: Runs server-side before upload; return metadata or throw to reject
- **Callbacks**: `onUploadComplete` receives `{ metadata, file: { url, name, size, key } }`
- **Client hooks**: `useUploadThing` for custom UIs, `useDropzone` for drag-and-drop
- **SSR ready**: `generateComponents`/`generateReactHelpers` for type-safe client utils
- **Delete**: `utapi.deleteFiles(fileKey)` via `uploadthing/server` for server-side file removal
- **CSS**: Import `@uploadthing/react/styles.css` or use `appearance` prop for custom styling

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.