---
title: Uploads
label: Overview
order: 10
desc: Payload supports uploads, storage and management of files directly on your server, combined with powerful file access control.
keywords: uploads, images, media, overview, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---

<Banner>
  Payload provides everything you need to enable file upload, storage, and
  management directly on your server—including extremely powerful file [access
  control](#access-control).
</Banner>

<LightDarkImage
  srcLight="https://payloadcms.com/images/docs/upload-admin.jpg"
  srcDark="https://payloadcms.com/images/docs/upload-admin.jpg"
  alt="Shows an Upload enabled collection in the Payload Admin Panel"
  caption="Admin Panel screenshot depicting a Media Collection with Upload enabled"
/>

**Here are some common use cases of Uploads:**

- Creating a "Media Library" that contains images for use throughout your site or app
- Building a Gated Content library where users need to sign up to gain access to downloadable assets like ebook PDFs, whitepapers, etc.
- Storing publicly available, downloadable assets like software, ZIP files, MP4s, etc.

**By simply enabling Upload functionality on a Collection, Payload will automatically transform your Collection into a robust file management / storage solution. The following modifications will be made:**

1. `filename`, `mimeType`, and `filesize` fields will be automatically added to your Collection. Optionally, if you pass `imageSizes` to your Collection's Upload config, a [`sizes`](#image-sizes) array will also be added containing auto-resized image sizes and filenames.
1. The Admin Panel will modify its built-in `List` component to show a thumbnail for each upload within the List View
1. The Admin Panel will modify its `Edit` view(s) to add a new set of corresponding Upload UI which will allow for file upload
1. The `create`, `update`, and `delete` Collection operations will be modified to support file upload, re-upload, and deletion

## Enabling Uploads

Every Payload Collection can opt-in to supporting Uploads by specifying the `upload` property on the Collection's config to either `true` or to an object containing `upload` options.

<Banner type="success">
  **Tip:**

A common pattern is to create a **"media"** collection and enable **upload** on that collection.

</Banner>

```ts
import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    staticDir: 'media',
    imageSizes: [
      {
        name: 'thumbnail',
        width: 400,
        height: 300,
        position: 'centre',
      },
      {
        name: 'card',
        width: 768,
        height: 1024,
        position: 'centre',
      },
      {
        name: 'tablet',
        width: 1024,
        // By specifying `undefined` or leaving a height undefined,
        // the image will be sized to a certain width,
        // but it will retain its original aspect ratio
        // and calculate a height automatically.
        height: undefined,
        position: 'centre',
      },
    ],
    adminThumbnail: 'thumbnail',
    mimeTypes: ['image/*'],
  },
  fields: [
    {
      name: 'alt',
      type: 'text',
    },
  ],
}
```

### Collection Upload Options

_An asterisk denotes that an option is required._

| Option                         | Description                                                                                                                                                                                                                                        |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`adminThumbnail`**           | Set the way that the [Admin Panel](../admin/overview) will display thumbnails for this Collection. [More](#admin-thumbnails)                                                                                                                       |
| **`bulkUpload`**               | Allow users to upload in bulk from the list view, default is true                                                                                                                                                                                  |
| **`cacheTags`**                | Set to `false` to disable the cache tag set in the UI for the admin thumbnail component. Useful for when CDNs don't allow certain cache queries.                                                                                                   |
| **`crop`**                     | Set to `false` to disable the cropping tool in the [Admin Panel](../admin/overview). Crop is enabled by default. [More](#crop-and-focal-point-selector)                                                                                            |
| **`disableLocalStorage`**      | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage)                                                                                                                                                        |
| **`displayPreview`**           | Enable displaying preview of the uploaded file in Upload fields related to this Collection. Can be locally overridden by `displayPreview` option in Upload field. [More](/docs/fields/upload#config-options).                                      |
| **`externalFileHeaderFilter`** | Accepts existing headers and returns the headers after filtering or modifying.                                                                                                                                                                     |
| **`filesRequiredOnCreate`**    | Mandate file data on creation, default is true.                                                                                                                                                                                                    |
| **`filenameCompoundIndex`**    | Field slugs to use for a compound index instead of the default filename index.                                                                                                                                                                     |
| **`focalPoint`**               | Set to `false` to disable the focal point selection tool in the [Admin Panel](../admin/overview). The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector)              |
| **`formatOptions`**            | An object with `format` and `options` that are used with the Sharp image library to format the upload file. [More](https://sharp.pixelplumbing.com/api-output#toformat)                                                                            |
| **`handlers`**                 | Array of Request handlers to execute when fetching a file, if a handler returns a Response it will be sent to the client. Otherwise Payload will retrieve and send back the file.                                                                  |
| **`imageSizes`**               | If specified, image uploads will be automatically resized in accordance to these image sizes. [More](#image-sizes)                                                                                                                                 |
| **`mimeTypes`**                | Restrict mimeTypes in the file picker. Array of valid mimetypes or mimetype wildcards [More](#mimetypes)                                                                                                                                           |
| **`pasteURL`**                 | Controls whether files can be uploaded from remote URLs by pasting them into the Upload field. **Enabled by default.** Accepts `false` to disable or an object with an `allowList` of valid remote URLs. [More](#uploading-files-from-remote-urls) |
| **`resizeOptions`**            | An object passed to the the Sharp image library to resize the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize)                                                                                                                    |
| **`staticDir`**                | The folder directory to use to store media in. Can be either an absolute path or relative to the directory that contains your config. Defaults to your collection slug                                                                             |
| **`trimOptions`**              | An object passed to the the Sharp image library to trim the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize#trim)                                                                                                                 |
| **`withMetadata`**             | If specified, appends metadata to the output image file. Accepts a boolean or a function that receives `metadata` and `req`, returning a boolean.                                                                                                  |
| **`hideFileInputOnCreate`**    | Set to `true` to prevent the admin UI from showing file inputs during document creation, useful for programmatic file generation.                                                                                                                  |
| **`hideRemoveFile`**           | Set to `true` to prevent the admin UI having a way to remove an existing file while editing.                                                                                                                                                       |

### Payload-wide Upload Options

Upload options are specifiable on a Collection by Collection basis, you can also control app wide options by passing your base Payload Config an `upload` property containing an object supportive of all `Busboy` configuration options.

| Option                   | Description                                                                                                                                                                        |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`abortOnLimit`**       | A boolean that, if `true`, returns HTTP 413 if a file exceeds the file size limit. If `false`, the file is truncated. Defaults to `false`.                                         |
| **`createParentPath`**   | Set to `true` to automatically create a directory path when moving files from a temporary directory or buffer. Defaults to `false`.                                                |
| **`debug`**              | A boolean that turns upload process logging on if `true`, or off if `false`. Useful for troubleshooting. Defaults to `false`.                                                      |
| **`limitHandler`**       | A function which is invoked if the file is greater than configured limits.                                                                                                         |
| **`parseNested`**        | Set to `true` to turn `req.body` and `req.files` into nested structures. By default `req.body` and `req.files` are flat objects. Defaults to `false`.                              |
| **`preserveExtension`**  | Preserves file extensions with the `safeFileNames` option. Limits file names to 3 characters if `true` or a custom length if a `number`, trimming from the start of the extension. |
| **`responseOnLimit`**    | A `string` that is sent in the Response to a client if the file size limit is exceeded when used with `abortOnLimit`.                                                              |
| **`safeFileNames`**      | Set to `true` to strip non-alphanumeric characters except dashes and underscores. Can also be set to a regex to determine what to strip. Defaults to `false`.                      |
| **`tempFileDir`**        | A `string` path to store temporary files used when the `useTempFiles` option is set to `true`. Defaults to `'./tmp'`.                                                              |
| **`uploadTimeout`**      | A `number` that defines how long to wait for data before aborting, specified in milliseconds. Set to `0` to disable timeout checks. Defaults to `60000`.                           |
| **`uriDecodeFileNames`** | Set to `true` to apply uri decoding to file names. Defaults to `false`.                                                                                                            |
| **`useTempFiles`**       | Set to `true` to store files to a temporary directory instead of in RAM, reducing memory usage for large files or many files.                                                      |

[Click here](https://github.com/mscdex/busboy#api) for more documentation about what you can control with `Busboy`.

A common example of what you might want to customize within Payload-wide Upload options would be to increase the allowed `fileSize` of uploads sent to Payload:

```ts
import { buildConfig } from 'payload'

export default buildConfig({
  collections: [
    {
      slug: 'media',
      fields: [
        {
          name: 'alt',
          type: 'text',
        },
      ],
      upload: true,
    },
  ],
  upload: {
    limits: {
      fileSize: 5000000, // 5MB, written in bytes
    },
  },
})
```

### Custom filename via hooks

You can customize the filename before it's uploaded to the server by using a `beforeOperation` hook.

```ts
beforeOperation: [
  ({ req, operation }) => {
    if ((operation === 'create' || operation === 'update') && req.file) {
      req.file.name = 'test.jpg'
    }
  },
],
```

The `req.file` object will have additional information about the file, such as mimeType and extension, and you also have full access to the file data itself.
The filename from here will also be threaded to image sizes if they're enabled.

## Image Sizes

If you specify an array of `imageSizes` to your `upload` config, Payload will automatically crop and resize your uploads to fit each of the sizes specified by your config.

The [Admin Panel](../admin/overview) will also automatically display all available files, including width, height, and file size, for each of your uploaded files.

Behind the scenes, Payload relies on [`sharp`](https://sharp.pixelplumbing.com/api-resize#resize) to perform its image resizing. You can specify additional options for `sharp` to use while resizing your images.

Note that for image resizing to work, `sharp` must be specified in your [Payload Config](../configuration/overview). This is configured by default if you created your Payload project with `create-payload-app`. See `sharp` in [Config Options](../configuration/overview#config-options).

#### Accessing the resized images in hooks

All auto-resized images are exposed to be re-used in hooks and similar via an object that is bound to `req.payloadUploadSizes`.

The object will have keys for each size generated, and each key will be set equal to a buffer containing the file data.

#### Handling Image Enlargement

When an uploaded image is smaller than the defined image size, we have 3 options:

`withoutEnlargement: undefined | false | true`

1. `undefined` [default]: uploading images with smaller width AND height than the image size will return null
2. `false`: always enlarge images to the image size
3. `true`: if the image is smaller than the image size, return the original image

<Banner type="error">
  **Note:**

By default, the image size will return NULL when the uploaded image is smaller than the defined
image size. Use the `withoutEnlargement` prop to change this.

</Banner>

#### Custom file name per size

Each image size supports a `generateImageName` function that can be used to generate a custom file name for the resized image.
This function receives the original file name, the resize name, the extension, height and width as arguments.

```ts
{
  name: 'thumbnail',
  width: 400,
  height: 300,
  generateImageName: ({ height, sizeName, extension, width }) => {
    return `custom-${sizeName}-${height}-${width}.${extension}`
  },
}
```

## Crop and Focal Point Selector

This feature is only available for image file types.

Setting `crop: false` and `focalPoint: false` in your Upload config will be disable the respective selector in the [Admin Panel](../admin/overview).

Image cropping occurs before any resizing, the resized images will therefore be generated from the cropped image (**not** the original image).

If no resizing options are specified (`imageSizes` or `resizeOptions`), the focal point selector will not be displayed.

## Disabling Local Upload Storage

If you are using a plugin to send your files off to a third-party file storage host or CDN, like Amazon S3 or similar, you may not want to store your files locally at all. You can prevent Payload from writing files to disk by specifying `disableLocalStorage: true` on your collection's upload config.

<Banner type="warning">
  **Note:**

This is a fairly advanced feature. If you do disable local file storage, by default, your admin
panel's thumbnails will be broken as you will not have stored a file. It will be totally up to you
to use either a plugin or your own hooks to store your files in a permanent manner, as well as
provide your own admin thumbnail using **upload.adminThumbnail**.

</Banner>

## Admin Thumbnails

You can specify how Payload retrieves admin thumbnails for your upload-enabled Collections with one of the following:

1. `adminThumbnail` as a **string**, equal to one of your provided image size names.

```ts
import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    // highlight-start
    adminThumbnail: 'small',
    // highlight-end
    imageSizes: [
      {
        name: 'small',
        fit: 'cover',
        height: 300,
        width: 900,
      },
      {
        name: 'large',
        fit: 'cover',
        height: 600,
        width: 1800,
      },
    ],
  },
}
```

2. `adminThumbnail` as a **function** that takes the document's data and sends back a full URL to load the thumbnail.

```ts
import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    // highlight-start
    adminThumbnail: ({ doc }) =>
      `https://google.com/custom-path-to-file/${doc.filename}`,
    // highlight-end
  },
}
```

## MimeTypes

Specifying the `mimeTypes` property can restrict what files are allowed from the user's file picker. This accepts an array of strings, which can be any valid mimetype or mimetype wildcards

Some example values are: `image/*`, `audio/*`, `video/*`, `image/png`, `application/pdf`

**Example mimeTypes usage:**

```ts
import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    mimeTypes: ['image/*', 'application/pdf'], // highlight-line
  },
}
```

## Uploading Files

<Banner type="warning">
  **Important:**

Uploading files is currently only possible through the REST and Local APIs due to how GraphQL
works. It's difficult and fairly nonsensical to support uploading files through GraphQL.

</Banner>

To upload a file, use your collection's [`create`](/docs/rest-api/overview#collections) endpoint. Send it all the data that your Collection requires, as well as a `file` key containing the file that you'd like to upload.

Send your request as a `multipart/form-data` request, using [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) if possible.

<Banner type="info">
  **Note:** To include any additional fields (like `title`, `alt`, etc.), append
  a `_payload` field containing a JSON-stringified object of the required
  values. These values must match the schema of your upload-enabled collection.
</Banner>

```ts
const fileInput = document.querySelector('#your-file-input')
const formData = new FormData()

formData.append('file', fileInput.files[0])

// Replace with the fields defined in your upload-enabled collection.
// The example below includes an optional field like 'title'.
formData.append(
  '_payload',
  JSON.stringify({
    title: 'Example Title',
    description: 'An optional description for the file',
  }),
)

fetch('api/:upload-slug', {
  method: 'POST',
  body: formData,
  /**
   * Do not manually add the Content-Type Header
   * the browser will handle this.
   *
   * headers: {
   *  'Content-Type': 'multipart/form-data'
   * }
   */
})
```

## Uploading Files stored locally

If you want to upload a file stored on your machine directly using the `payload.create` method, for example, during a seed script,
you can use the `filePath` property to specify the local path of the file.

```ts
const localFilePath = path.resolve(__dirname, filename)

await payload.create({
  collection: 'media',
  data: {
    alt,
  },
  filePath: localFilePath,
})
```

The `data` property should still include all the required fields of your `media` collection.

<Banner type="warning">
  **Important:**

Remember that all custom hooks attached to the `media` collection will still trigger.
Ensure that files match the specified mimeTypes or sizes defined in the collection's `formatOptions` or custom `hooks`.

</Banner>

## Uploading Files from Remote URLs

The `pasteURL` option allows users to fetch files from remote URLs by pasting them into an Upload field. This option is **enabled by default** and can be configured to either **allow unrestricted client-side fetching** or **restrict server-side fetching** to specific trusted domains.

By default, Payload uses **client-side fetching**, where the browser downloads the file directly from the provided URL. However, **client-side fetching will fail if the URL’s server has CORS restrictions**, making it suitable only for internal URLs or public URLs without CORS blocks.

To fetch files from **restricted URLs** that would otherwise be blocked by CORS, use **server-side fetching** by configuring the `pasteURL` option with an `allowList` of trusted domains. This method ensures that Payload downloads the file on the server and streams it to the browser. However, for security reasons, only URLs that match the specified `allowList` will be allowed.

#### Configuration Example

Here’s how to configure the pasteURL option to control remote URL fetching:

```
import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    pasteURL: {
      allowList: [
        {
          hostname: 'payloadcms.com', // required
          pathname: '',
          port: '',
          protocol: 'https',
          search: ''
        },
        {
          hostname: 'example.com',
          pathname: '/images/*',
        },
      ],
    },
  },
}
```

##### Accepted Values for `pasteURL`

| Option          | Description                                                                                                                                                    |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`undefined`** | Default behavior. Enables client-side fetching for internal or public URLs.                                                                                    |
| **`false`**     | Disables the ability to paste URLs into Upload fields.                                                                                                         |
| **`allowList`** | Enables server-side fetching for specific trusted URLs. Requires an array of objects defining trusted domains. See the table below for details on `AllowItem`. |

##### `AllowItem` Properties

_An asterisk denotes that an option is required._

| Option            | Description                                                                                          | Example       |
| ----------------- | ---------------------------------------------------------------------------------------------------- | ------------- |
| **`hostname`** \* | The hostname of the allowed URL. This is required to ensure the URL is coming from a trusted source. | `example.com` |
| **`pathname`**    | The path portion of the URL. Supports wildcards to match multiple paths.                             | `/images/*`   |
| **`port`**        | The port number of the URL. If not specified, the default port for the protocol will be used.        | `3000`        |
| **`protocol`**    | The protocol to match. Must be either `http` or `https`. Defaults to `https`.                        | `https`       |
| **`search`**      | The query string of the URL. If specified, the URL must match this exact query string.               | `?version=1`  |

## Access Control

All files that are uploaded to each Collection automatically support the `read` [Access Control](/docs/access-control/overview) function from the Collection itself. You can use this to control who should be allowed to see your uploads, and who should not.
