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.
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}.jsonWhat 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-uploadThe 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-routeThis 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:
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-configimport { 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
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/reactWithout the namespace
You can also install any item by URL:
npx shadcn@latest add https://uploadcn.dev/r/upload.json