# Next.js (/docs/frameworks/nextjs)



<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @uploadcn/file-upload @uploadcn/upload-route
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @uploadcn/file-upload @uploadcn/upload-route
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @uploadcn/file-upload @uploadcn/upload-route
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @uploadcn/file-upload @uploadcn/upload-route
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Route handler [#route-handler]

`@uploadcn/server` uses Web standard `Request`/`Response`, so the route is one line:

```ts title="app/api/upload/route.ts"
import { createUploadRoute, s3Storage } from "@uploadcn/server"

export const { POST } = createUploadRoute({
  storage: s3Storage({
    bucket: process.env.S3_BUCKET!,
    region: process.env.S3_REGION!,
    accessKeyId: process.env.S3_ACCESS_KEY_ID!,
    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
  }),
})
```

It runs on the Node.js and Edge runtimes.

## Client components [#client-components]

Upload components use hooks, so they're Client Components. The registry files already
include `"use client"`; render them from Server Components as usual:

```tsx title="app/settings/page.tsx"
import { AvatarUpload } from "@/components/avatar-upload"

export default async function SettingsPage() {
  const user = await getUser()
  return <AvatarUpload src={user.image} />
}
```

## Server actions [#server-actions]

Submit object keys rather than files, see [Forms](/docs/guides/forms#native-forms-and-server-actions).
Next.js limits server action bodies to 1 MB by default, which is another reason to
upload directly to storage.

## App-wide uploads [#app-wide-uploads]

Put `UploadProvider` in `app/layout.tsx` so uploads survive navigation:

```tsx title="app/layout.tsx"
import { GlobalDropzone, UploadProvider, UploadQueuePanel } from "@/components/global-upload"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <UploadProvider persist>
          {children}
          <GlobalDropzone />
          <UploadQueuePanel />
        </UploadProvider>
      </body>
    </html>
  )
}
```
