Installation

Add UploadCN to a shadcn project.

UploadCN works with any project set up with the shadcn CLI (Next.js, Vite, TanStack Start, React Router, Astro…), with either Radix or Base UI primitives and Tailwind CSS v4.

Set up shadcn/ui

Skip this if your project already has a components.json.

npx shadcn@latest init

Register the @uploadcn namespace

This adds UploadCN to the registries in your components.json, so you can install items by name.

npx shadcn@latest registry add @uploadcn=https://uploadcn.dev/r/{style}/{name}.json

What this changes

It adds "@uploadcn": "https://uploadcn.dev/r/{style}/{name}.json" to the registries field of components.json. The CLI fills in {style} from your project (base-nova, radix-lyra, aria-maia…), so every component arrives in your style. Icons are converted to your iconLibrary on install.

Add a component

npx shadcn@latest add @uploadcn/file-upload

The CLI copies components/ui/upload.tsx and components/file-upload.tsx into your project and installs @uploadcn/react and @uploadcn/core.

Choose where files go

Every component uploads through an adapter. Pick the one for your storage; you can change it later without touching a single component.

Files go straight from the browser to your bucket; a small route signs each upload.

npx shadcn@latest add @uploadcn/upload-route

This creates app/api/upload/route.ts (or use @uploadcn/upload-route-tanstack). Fill in your credentials, and allow PUT from your origin in the bucket's CORS settings, exposing the ETag header:

.env.local
S3_BUCKET=my-uploads
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=…
S3_SECRET_ACCESS_KEY=…
const adapter = s3Adapter({ endpoint: "/api/upload" })

See Amazon S3 and Cloudflare R2.

Configure it once

Add the config wrapper and put it in your root layout. Every UploadCN component below it uses that adapter.

npx shadcn@latest add @uploadcn/upload-config
app/layout.tsx
import { UploadConfig } from "@/components/upload-config"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <UploadConfig>{children}</UploadConfig>
      </body>
    </html>
  )
}

Edit the adapter in components/upload-config.tsx. A component's own adapter prop still wins, so one page can upload somewhere else.

Use it

app/page.tsx
import { FileUpload } from "@/components/file-upload"

export default function Page() {
  return <FileUpload accept="image/*" maxSize={10_000_000} maxFiles={5} />
}

Installing without the CLI

Everything is plain source. Install the packages, then copy the component source from each component page (the Manual tab).

npm install @uploadcn/core @uploadcn/react

Without the namespace

You can also install any item by URL:

npx shadcn@latest add https://uploadcn.dev/r/upload.json

On this page