Next.js

File uploads for Next.js

UploadKit is an upload SDK for the Next.js App Router. You define typed file routes in a Route Handler, drop in React upload components, and the browser sends files directly to storage with presigned URLs. Use UploadKit's managed storage or your own S3-compatible bucket.

Quickstart

Set up uploads in a Next.js App Router project

This follows the Next.js getting-started guide. It needs Next.js 14 or later and React 18 or later. To start from a ready project, run npx create-uploadkit-app my-app --template next. To add UploadKit to an existing project, run npx uploadkit init.

1. Install

pnpm add @uploadkitdev/next @uploadkitdev/react

2. Define the file router

Every upload category lives in one Route Handler with its size limit, allowed types and server-side logic. The middleware runs on your server before an upload is authorised, and whatever it returns is passed to onUploadComplete as typed metadata.

app/api/uploadkit/route.ts
import { createUploadKitHandler } from '@uploadkitdev/next';
import type { FileRouter } from '@uploadkitdev/next';
import { auth } from '@/lib/auth'; // your Auth.js or Clerk auth helper

const router = {
  // Accept profile images up to 4 MB
  imageUploader: {
    maxFileSize: '4MB',
    allowedTypes: ['image/*'],
    onUploadComplete: async ({ file, metadata }) => {
      console.log('Image uploaded by user:', metadata.userId, file.url);
    },
  },

  // PDF / Word documents up to 16 MB, max 5 files at once
  documentUploader: {
    maxFileSize: '16MB',
    maxFileCount: 5,
    allowedTypes: ['application/pdf', 'application/msword'],
    middleware: async ({ req }) => {
      const session = await auth();
      if (!session?.user?.id) {
        throw new Error('Unauthorized');
      }
      // Anything returned here is passed to onUploadComplete as `metadata`
      return { userId: session.user.id };
    },
    onUploadComplete: async ({ file, metadata }) => {
      console.log('Document uploaded:', file.key, 'by', metadata.userId);
    },
  },
} satisfies FileRouter;

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

3. Add the API key

.env.local
# Server-side only. Never prefix this with NEXT_PUBLIC_
UPLOADKIT_API_KEY=uk_live_xxxxxxxxxxxxxxxxxxxxx

4. Wrap the layout with the provider

app/layout.tsx
import { UploadKitProvider } from '@uploadkitdev/react';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <UploadKitProvider endpoint="/api/uploadkit">
          {children}
        </UploadKitProvider>
      </body>
    </html>
  );
}

5. Use a component

app/documents/page.tsx
import { UploadDropzone } from '@uploadkitdev/react';

export default function DocumentsPage() {
  return (
    <UploadDropzone
      route="documentUploader"
      onUploadComplete={(files) => {
        console.log('Documents uploaded:', files);
      }}
    />
  );
}
What you get

What the Next.js SDK includes

@uploadkitdev/next is the server half and @uploadkitdev/react is the UI half.

  • React upload components

    UploadButton, UploadDropzone and UploadModal work in App Router pages, and the SDK exports more than 40 components in total.

  • Type-safe file routes

    Declare the router with satisfies FileRouter and the route prop on every component is typed to your route names.

  • Presigned direct-to-storage uploads

    Your handler authorises the request, then the browser uploads the file to storage without proxying the bytes through Next.js.

  • Bring your own storage

    Pass a storage config to createUploadKitHandler to use Cloudflare R2, AWS S3, MinIO or another S3-compatible bucket. The frontend code does not change.

Typed components

Bind the components to your router type once, then import them from that module.

lib/uploadkit.ts
import { generateReactHelpers } from '@uploadkitdev/react';
import type { FileRouter } from '@/app/api/uploadkit/route';

// Re-export typed components — route prop is now typed to your route names
export const { UploadButton, UploadDropzone, UploadModal, useUploadKit } =
  generateReactHelpers<FileRouter>();

Your own bucket

In self-hosted BYOS mode you supply either an apiKey or a storage object, not both. Credentials stay in your server environment. The managed UploadKit Cloud path and the self-hosted path are chosen per deployment, and they are not mixed inside one Cloud project.

app/api/uploadkit/route.ts (BYOS with Cloudflare R2)
export const { GET, POST } = createUploadKitHandler({
  router,
  storage: {
    endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
    region: 'auto',
    accessKeyId: process.env.R2_ACCESS_KEY_ID!,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
    bucket: process.env.R2_BUCKET_NAME!,
  },
});

For the details of the S3 side of this flow, read how to upload files to Cloudflare R2 in Next.js, or the longer walkthrough with sessions and a database in file uploads in Next.js with Auth.js and MongoDB.

Limits

Limits per plan

These are the current plan limits. Full details are on the pricing page.

  • Free

    5 GB storage, 2 GB bandwidth per month, 4 MB max file, 1,000 uploads per month, 2 projects.

  • Pro — $15/mo

    100 GB storage, 200 GB bandwidth, 512 MB max file, 50,000 uploads per month, 10 projects.

  • Team — $35/mo

    1 TB storage, 2 TB bandwidth, 5 GB max file, 500,000 uploads per month, 50 projects.

  • Enterprise

    Custom limits. See the pricing page.

FAQ

Frequently asked questions

Does UploadKit work with the Next.js Pages Router?

No. The SDK and the CLI target the App Router. When uploadkit init finds a Pages Router project (pages/_app) without an app directory, it stops with the message "Pages Router not supported — migrate to App Router". The docs list Next.js 14 or later with the App Router as the prerequisite. Next.js setup guide

How do I upload to S3 from Next.js with UploadKit?

Pass a storage object (bucket, region, accessKeyId, secretAccessKey, and an endpoint for non-AWS providers) to createUploadKitHandler. Files go to your bucket and the React components stay the same. BYOS works with any S3-compatible storage; the docs show Cloudflare R2, AWS S3 and MinIO. BYOS guide

Do uploads pass through my Next.js server?

No for the file bytes. Your route handler authorises the upload and the browser sends the file straight to storage with a presigned URL, so the file bytes never count against the request body limit of your hosting platform. See the post on presigned URLs versus server proxy uploads for the trade-offs. Presigned URLs vs server proxy

Is the route typed end to end?

Yes. Define the router with satisfies FileRouter and pass its type to generateReactHelpers. The route prop of UploadButton, UploadDropzone and UploadModal is then typed to your route names.

Which components do I get?

UploadButton, UploadDropzone and UploadModal are covered in the Next.js guide, and the SDK exports more than 40 React components in total. Each one reads the endpoint from UploadKitProvider. Browse components

What does it cost?

The free plan includes 5 GB storage, 2 GB bandwidth per month, a 4 MB maximum file size, 1,000 uploads per month and 2 projects. Pro is $15 per month and Team is $35 per month. With self-hosted BYOS your files and storage bill stay with your own provider. See pricing

Add uploads to your Next.js app

Start on the free plan: 5 GB storage, 2 GB bandwidth per month, no credit card.