UploadKit
Guides

Migration from UploadThing

Step-by-step migration guide from UploadThing to UploadKit — API equivalents, prop renames, and new features.

UploadKit is a direct UploadThing alternative built on the same presigned URL approach. Most apps migrate in under an hour. This guide maps every UploadThing concept to its UploadKit equivalent.

API Equivalents

UploadThingUploadKitNotes
createUploadthing()createUploadKitHandler()Handler factory function
f() chainsatisfies FileRouter objectObject syntax instead of chain
useUploadThing()useUploadKit()Same hook API, different name
<UploadButton endpoint="..."><UploadButton route="...">endpointroute prop
<UploadDropzone endpoint="..."><UploadDropzone route="...">endpointroute prop
UTApicreateUploadKit() (core SDK)Server-side file operations
.middleware()middleware: (ctx) => {}Inline function in route config
.onUploadComplete()onUploadComplete: (args) => {}Inline function in route config
@uploadthing/next@uploadkitdev/nextPackage rename
@uploadthing/react@uploadkitdev/reactPackage rename

Key Differences

1. File router syntax

UploadThing uses a builder chain:

// UploadThing
const f = createUploadthing();

const ourFileRouter = {
  imageUploader: f({ image: { maxFileSize: '4MB' } })
    .middleware(async ({ req }) => {
      const user = await auth(req);
      return { userId: user.id };
    })
    .onUploadComplete(async ({ metadata, file }) => {
      console.log('file url', file.url);
    }),
};

UploadKit uses a plain object with satisfies:

// UploadKit
const router = {
  imageUploader: {
    maxFileSize: '4MB',
    allowedTypes: ['image/*'],
    middleware: async ({ req }) => {
      const user = await auth(req);
      return { userId: user.id };
    },
    onUploadComplete: async ({ file, metadata }) => {
      console.log('file url', file.url);
    },
  },
} satisfies FileRouter;

2. endpoint vs route prop

// UploadThing
<UploadButton endpoint="imageUploader" />

// UploadKit
<UploadButton route="imageUploader" />

3. BYOS mode

UploadThing does not support bring-your-own-storage. UploadKit lets you use your own S3 or R2 bucket with zero changes to your frontend code:

// UploadKit only — server-side BYOS config
export const { GET, POST } = createUploadKitHandler({
  router,
  storage: {
    provider: 'r2',
    accountId: process.env.CF_ACCOUNT_ID!,
    accessKeyId: process.env.R2_ACCESS_KEY_ID!,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
    bucket: process.env.R2_BUCKET_NAME!,
    publicUrl: process.env.R2_PUBLIC_URL!,
  },
});

4. CSS theming

UploadThing requires per-component class overrides. UploadKit supports CSS custom properties for global theming — one declaration in globals.css styles all components:

/* UploadKit — global theme override */
:root {
  --uk-accent: #6366f1;
  --uk-radius: 8px;
}

Step-by-step migration

Step 1: Replace packages

# Remove UploadThing packages
pnpm remove uploadthing @uploadthing/next @uploadthing/react

# Install UploadKit packages
pnpm add @uploadkitdev/next @uploadkitdev/react

Step 2: Update the file router

app/api/uploadthing/core.ts
import { createUploadthing, type FileRouter } from 'uploadthing/next';

const f = createUploadthing();

export const ourFileRouter = {
  imageUploader: f({ image: { maxFileSize: '4MB' } })
    .middleware(async ({ req }) => {
      const user = await auth(req);
      if (!user) throw new Error('Unauthorized');
      return { userId: user.id };
    })
    .onUploadComplete(async ({ metadata, file }) => {
      await db.files.create({ url: file.url, userId: metadata.userId });
    }),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;
app/api/uploadkit/route.ts
import { createUploadKitHandler } from '@uploadkitdev/next';
import type { FileRouter } from '@uploadkitdev/next';

const router = {
  imageUploader: {
    maxFileSize: '4MB',
    allowedTypes: ['image/*'],
    middleware: async ({ req }) => {
      const user = await auth(req);
      if (!user) throw new Error('Unauthorized');
      return { userId: user.id };
    },
    onUploadComplete: async ({ file, metadata }) => {
      await db.files.create({ url: file.url, userId: metadata.userId });
    },
  },
} satisfies FileRouter;

export const { GET, POST } = createUploadKitHandler({ router });

Step 3: Update component imports and props

import { UploadButton, UploadDropzone } from '@uploadthing/react';

<UploadButton endpoint="imageUploader" />
<UploadDropzone endpoint="imageUploader" />
import { UploadButton, UploadDropzone } from '@uploadkitdev/react';

<UploadButton route="imageUploader" />
<UploadDropzone route="imageUploader" />

Step 4: Update environment variables

# Remove UploadThing variables
UPLOADTHING_SECRET=...
UPLOADTHING_APP_ID=...

# Add UploadKit variable (server-side only — never exposed to browser)
UPLOADKIT_API_KEY=uk_live_xxxxxxxxxxxxxxxxxxxxx

Step 5: Update the API route path

If you have hardcoded the UploadThing API route path anywhere (e.g., in the UploadThing client or custom fetch calls):

# UploadThing
/api/uploadthing

# UploadKit
/api/uploadkit

Step 6: Test the upload flow

  1. Start your dev server
  2. Try a file upload through your UI
  3. Verify the file appears in your UploadKit dashboard at app.uploadkit.dev
  4. Check that onUploadComplete fires and your database is updated

What's new in UploadKit

Beyond the migration, UploadKit adds several capabilities that UploadThing does not offer:

BYOS (Bring Your Own Storage) — Use your own S3 or Cloudflare R2 bucket. Your files never touch UploadKit's infrastructure, but you still get the full SDK and dashboard experience.

More generous free tier — 5 GB storage, 2 GB bandwidth, and 1,000 uploads per month on the free plan. No credit card required.

CSS variable theming — One global CSS override styles all components. No need to copy class lists to every component.

Open-source SDK@uploadkitdev/core, @uploadkitdev/react, and @uploadkitdev/next are all MIT licensed and published on npm.

Need help with your migration? Open a GitHub discussion or reach out at support@uploadkit.dev.

After migration: quickstart reference

Once migrated, you can follow the Quickstart guide for a clean reference of the full UploadKit setup.

On this page