# Image Upload (/docs/components/image-upload)



<ComponentPreview name="image-upload" />

## Installation [#installation]

<Tabs items="[&#x22;Command&#x22;, &#x22;Manual&#x22;]">
  <Tab value="Command">
    <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/image-upload
        ```
      </CodeBlockTab>

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

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

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

  <Tab value="Manual">
    Install [Upload](/docs/components/upload) first, then copy:

    <ComponentSource name="image-upload" />
  </Tab>
</Tabs>

## Usage [#usage]

```tsx
import { ImageUpload } from "@/components/image-upload"
```

```tsx
<ImageUpload crop compress maxWidth={2000} />
```

Cropping happens inside the upload pipeline: each image waits in the `validating` state while its crop dialog is open, and several images are cropped one after another. Skipping a crop uploads the original.

## Props [#props]

<TypeTable
  type="{
  adapter: {
    type: &#x22;UploadAdapter&#x22;,
    default: 's3Adapter({ endpoint: &#x22;/api/upload&#x22; })',
    description: &#x22;Where files go.&#x22;,
  },
  crop: {
    type: &#x22;boolean | { aspect?: number }&#x22;,
    default: &#x22;false&#x22;,
    description: &#x22;Open a crop dialog for every image.&#x22;,
  },
  compress: {
    type: &#x22;boolean&#x22;,
    default: &#x22;false&#x22;,
    description: &#x22;Resize and re-encode before uploading (WebP by default).&#x22;,
  },
  maxWidth: {
    type: &#x22;number&#x22;,
    default: &#x22;2048&#x22;,
    description: &#x22;Longest edge after compression or cropping.&#x22;,
  },
  maxHeight: { type: &#x22;number&#x22;, default: &#x22;2048&#x22; },
  quality: {
    type: &#x22;number&#x22;,
    default: &#x22;0.82&#x22;,
    description: &#x22;Encoder quality, 0 to 1.&#x22;,
  },
  type: {
    type: '&#x22;image/webp&#x22; | &#x22;image/jpeg&#x22; | &#x22;image/avif&#x22; | &#x22;image/png&#x22;',
    description: &#x22;Output format.&#x22;,
  },
  maxFiles: { type: &#x22;number&#x22;, default: &#x22;12&#x22; },
  maxSize: {
    type: &#x22;number&#x22;,
    default: &#x22;20 MB&#x22;,
    description: &#x22;Max size of the original file.&#x22;,
  },
  minWidth: {
    type: &#x22;number&#x22;,
    description: &#x22;Minimum width of the original image.&#x22;,
  },
  minHeight: { type: &#x22;number&#x22; },
  onSuccess: { type: &#x22;(item) => void&#x22; },
}"
/>

## Examples [#examples]

### Image crop [#image-crop]

<ComponentPreview name="image-crop" />

### Image compression [#image-compression]

<ComponentPreview name="image-compression" />
