Choosing storage

Every component works with every backend. Pick an adapter, configure it once, and switch any time.

UploadCN separates where files go from what the UI looks like. Components never talk to a storage provider directly; they hand files to an adapter. Change the adapter and every dropzone, button, gallery and animated component follows.

Pick an adapter

You want…UseServer code
Your own S3 / R2 / MinIO / Spaces / B2 buckets3Adapter + @uploadcn/serverOne route
CloudinarycloudinaryAdapterNone (unsigned) or one
Files on your own server's disks3Adapter + @uploadcn/server/fsOne route + a file handler
Your existing upload endpointhttpAdapterYours
An SDK, Supabase, Firebase, Vercel Blob…createAdapterWhatever the SDK needs
Only the UI, the form submits the fileslocalAdapterNone
Resumable uploads through a tus servertusAdapterA tus server

Configure it once

Wrap your app in UploadConfigProvider. Every component below it uses that adapter, and any other defaults you set, like maxSize or retry.

components/upload-config.tsx
"use client"

import { cloudinaryAdapter } from "@uploadcn/core"
import { UploadConfigProvider } from "@uploadcn/react"

const adapter = cloudinaryAdapter({
  cloudName: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME!,
  uploadPreset: "user-uploads",
})

export function UploadConfig({ children }: { children: React.ReactNode }) {
  return (
    <UploadConfigProvider adapter={adapter} maxSize={20_000_000}>
      {children}
    </UploadConfigProvider>
  )
}

npx shadcn@latest add @uploadcn/upload-config creates this file for you. Put <UploadConfig> in your root layout.

Overriding per component

Props win over the provider, so a single component can upload somewhere else:

<AvatarUpload adapter={s3Adapter({ endpoint: "/api/avatars" })} />

Providers nest, too: wrap an admin area in its own UploadConfigProvider to give it a different bucket or larger limits.

No adapter?

Without a provider or an adapter prop, components throw an error that tells you how to configure one. There is no hidden default endpoint.

What every adapter gets

The engine handles the hard parts the same way for every backend:

  • Queue and concurrency: three uploads at a time by default.
  • Retries: network errors and 5xx/429 responses retry with backoff; 4xx don't.
  • Cancel: the adapter receives an AbortSignal.
  • Progress: the adapter reports bytes; the UI shows percent, speed and time left.
  • Resume: adapters that save state (S3 multipart, Cloudinary chunks, tus) continue where they left off after a failure or a reload.

On this page