Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Uploads

Shows an Upload enabled collection in the Payload Admin Panel
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 declare image sizes as variants on sharpTransformer, a variants group will also be added containing auto-resized image sizes and filenames.
  2. The Admin Panel will modify its built-in List component to show a thumbnail for each upload within the List View
  3. The Admin Panel will modify its Edit view(s) to add a new set of corresponding Upload UI which will allow for file upload
  4. 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.

1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: {
6
staticDir: 'media',
7
adminThumbnail: 'thumbnail',
8
mimeTypes: ['image/*'],
9
},
10
fields: [
11
{
12
name: 'alt',
13
type: 'text',
14
},
15
],
16
}

Collection Upload Options

An asterisk denotes that an option is required.

Option

Description

adminThumbnail

Set the way that the Admin Panel will display thumbnails for this Collection. More

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. Crop is enabled by default. For a collection configured on sharpTransformer, a crop set there replaces this value. More

disableLocalStorage

Completely disable uploading files to disk locally. More

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.

externalFileHeaderFilter

Accepts existing headers and returns the headers after filtering or modifying. If using this option, you should handle the removal of any sensitive cookies (like payload-prefixed cookies) to prevent leaking session information to external services. By default, Payload automatically filters out payload-prefixed cookies when this option is not defined.

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. The selector is shown when a registered transformer configures image sizes or image adjustments (resizeOptions, formatOptions, and so on) for this collection, or when this is set to true. For a collection configured on sharpTransformer, a focalPoint set there replaces this value. More

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.

mimeTypes

Restrict mimeTypes in the file picker. Array of valid mimetypes or mimetype wildcards More

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

skipSafeFetch

Set to an allowList to skip the safe fetch check when fetching external files. Set to true to skip the safe fetch for all documents in this collection. Defaults to false.

allowRestrictedFileTypes

Set to true to allow restricted file types. If your Collection has defined mimeTypes, restricted file verification will be skipped. Defaults to false. More

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

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.

modifyResponseHeaders

Accepts an object with existing headers and allows you to manipulate the response headers for media files. More

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 true.

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.

requestSizeLimit

Maximum size in bytes for the complete raw multipart request, including files, fields, headers, and boundaries. Accepts a non-negative safe integer or Infinity. Defaults to 50 MiB. Set to Infinity to disable this limit.

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.

transformers

An ordered array of file transformers, such as sharpTransformer(), that process uploads and serve dynamically-transformed files. Registering any transformer changes the file endpoint — see File Transformers.

tempFileDir

A string path to store temporary files used when the useTempFiles option is set to true. Defaults to 'tmp' in the current working directory. Supports absolute paths.

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 for more documentation about what you can control with Busboy.

Multipart requests default to requestSizeLimit: 50 * 1024 * 1024 (50 MiB for the complete raw request), limits.fileSize: 20 * 1024 * 1024 (20 MiB per file), limits.files: 3, limits.fields: 20, and limits.fieldSize: 1024 * 1024 (1 MiB per field). The request-wide limit counts files, fields, headers, boundaries, and other multipart overhead before content is passed to the parser. It allows two files at the per-file maximum with room for metadata while bounding the complete request independently of the individual Busboy limits. Concurrent requests multiply memory and temporary-storage usage; choose lower limits when appropriate for your deployment.

Individual limits overrides preserve the other defaults. A non-negative safe integer requestSizeLimit overrides the aggregate cap, and requestSizeLimit: Infinity disables it intentionally. Invalid values are rejected before the request body is read. An undefined override retains the 50 MiB default. Explicit Busboy limit values, including Infinity, are also respected. Request size, field size, file count, field count, and configured part count limits reject with HTTP 413. File size limits reject by default; abortOnLimit: false opts into truncated files. Failed requests cancel body consumption and remove temporary files.

For example, to set a 75 MiB request-wide limit and a smaller per-file limit for uploads sent to Payload:

1
import { buildConfig } from 'payload'
2
3
export default buildConfig({
4
collections: [
5
{
6
slug: 'media',
7
fields: [
8
{
9
name: 'alt',
10
type: 'text',
11
},
12
],
13
upload: true,
14
},
15
],
16
upload: {
17
requestSizeLimit: 75 * 1024 * 1024, // 75 MiB for the complete multipart request
18
limits: {
19
fileSize: 5000000, // 5MB, written in bytes
20
},
21
},
22
})

Custom filename via hooks

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

1
beforeOperation: [
2
({ req, operation }) => {
3
if ((operation === 'create' || operation === 'update') && req.file) {
4
req.file.name = 'test.jpg'
5
}
6
},
7
],

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.

Variants

If you declare image sizes, Payload will automatically crop and resize your uploads to fit each of the sizes specified by your config.

The Admin Panel will also automatically display all available files, including width, height, and file size, for each of your uploaded files.

Image sizes — declared as variants, along with the rest of Sharp's image-resizing options — are configured through @payloadcms/transformer-sharp, Payload's official image-resizing transformer:

1
import { buildConfig } from 'payload'
2
import { sharpTransformer } from '@payloadcms/transformer-sharp'
3
4
export default buildConfig({
5
collections: [
6
/* your Media collection, with `upload: true` */
7
],
8
upload: {
9
transformers: [
10
sharpTransformer({
11
collections: {
12
media: {
13
variants: [
14
{
15
name: 'thumbnail',
16
width: 400,
17
height: 300,
18
position: 'centre',
19
},
20
{
21
name: 'card',
22
width: 768,
23
height: 1024,
24
position: 'centre',
25
},
26
{
27
name: 'tablet',
28
width: 1024,
29
// By specifying `undefined` or leaving a height undefined,
30
// the image will be sized to a certain width,
31
// but it will retain its original aspect ratio
32
// and calculate a height automatically.
33
height: undefined,
34
position: 'centre',
35
},
36
],
37
},
38
},
39
}),
40
],
41
},
42
})

sharpTransformer writes a Sharp-agnostic projection of each configured variants entry (name, admin, and generateImageName only) back onto the collection's sanitized upload.variants, so the rest of this section — and the generated variants shape on your documents — is unaffected by where the configuration is authored. Neither imageSizes nor variants can be set on the collection itself.

Admin List View Options

Each image size also supports admin options to control whether it appears in the Admin Panel list view.

1
{
2
name: 'thumbnail',
3
width: 400,
4
height: 300,
5
admin: {
6
disabled: {
7
groupBy: true, // hide from list view groupBy options
8
column: true, // hide from list view columns
9
filter: true, // hide from list view filters
10
},
11
},
12
}

Option

Description

disabled.groupBy

If set to true, this image size will not be available as a selectable groupBy option in the collection list view. Defaults to false.

disabled.column

If set to true, this image size will not be available as a selectable column in the collection list view. Defaults to false.

disabled.filter

If set to true, this image size will not be available as a filter option in the collection list view. Defaults to false.

This is useful for hiding large or rarely used image sizes from the list view UI while still keeping them available in the API.

Accessing the resized images in hooks

All auto-resized images are exposed to be reused 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

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.

1
{
2
name: 'thumbnail',
3
width: 400,
4
height: 300,
5
generateImageName: ({ height, sizeName, extension, width }) => {
6
return `custom-${sizeName}-${height}-${width}.${extension}`
7
},
8
}

Crop and Focal Point Selector

This feature is only available for image file types.

Setting crop: false and focalPoint: false on sharpTransformer({ collections }) for this collection will disable the respective selector in the Admin Panel.

Cropping changes the main image. Configured image sizes are generated from the retained original, so cropping the main image does not also crop those sizes. The focal point controls how configured sizes are framed.

The focal point selector is only displayed when the collection has image sizes (variants) or image adjustments (resizeOptions, formatOptions, trimOptions, constructorOptions, or withMetadata), or when focalPoint is explicitly true.

File Versioning

Payload retains the source of each new upload as original, even when the main image is processed. With versions enabled, saved versions also retain the stored files they reference until those versions are removed. Keeping originals and file history uses additional storage, especially when an image has several generated sizes.

Existing uploads are upgraded as they are edited; no bulk file migration is required. For an older image whose untouched source was already deleted, the existing stored file is the best available original. The upgrade cannot recover source bytes or historical files that were deleted before file versioning was enabled.

SQL projects need a schema migration; see the v4 migration guide.

To read a stored historical file through Payload's file endpoint, include ?version=<version-id>. Version reads generate these URLs for the main file, original, stored variants, and thumbnail. The requested filename and storage prefix must belong to that version, and both parent-document read access and version read access are enforced. Without version, the endpoint resolves current published files and the latest draft, subject to collection read access; filenames found only in history return 404. Historical requests serve the saved bytes without rerunning transformations. Public URLs supplied directly by a storage provider remain direct.

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.

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 image size names (declared as variants on sharpTransformer).
1
import { buildConfig } from 'payload'
2
import { sharpTransformer } from '@payloadcms/transformer-sharp'
3
4
export default buildConfig({
5
collections: [
6
{
7
slug: 'media',
8
fields: [],
9
upload: {
10
adminThumbnail: 'small',
11
},
12
},
13
],
14
upload: {
15
transformers: [
16
sharpTransformer({
17
collections: {
18
media: {
19
variants: [
20
{
21
name: 'small',
22
fit: 'cover',
23
height: 300,
24
width: 900,
25
},
26
{
27
name: 'large',
28
fit: 'cover',
29
height: 600,
30
width: 1800,
31
},
32
],
33
},
34
},
35
}),
36
],
37
},
38
})
  1. adminThumbnail as a function that takes the document's data and sends back a full URL to load the thumbnail.
1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: {
6
adminThumbnail: ({ doc }) =>
7
`https://google.com/custom-path-to-file/${doc.filename}`,
8
},
9
}

File Previews

When editing a document in an upload-enabled Collection, the Admin Panel renders a preview of the file in the upload side panel. The preview that's used is chosen automatically based on the file's MIME type:

File type

Built-in preview

image/*

Image thumbnail (falls back to a file icon)

video/*

Native HTML5 <video> player

audio/*

Native HTML5 <audio> player

application/pdf

The browser's built-in PDF viewer (in an iframe)

Everything else

Image thumbnail / generic file icon

These built-in previews require no configuration.

Custom File Previews

To render your own preview component in place of the built-in one, use the upload.admin.components.filePreview property. It accepts either a single component that renders for every file, or a map keyed by MIME type pattern so you can target specific file types.

A single component for all file types

1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: {
6
admin: {
7
components: {
8
filePreview: '/components/MediaPreview#MediaPreview',
9
},
10
},
11
},
12
fields: [],
13
}

A map keyed by MIME type

When filePreview is an object, each key is a MIME type pattern. Payload resolves the component to render in the following priority order:

  1. Exact match — e.g. application/pdf
  2. Category wildcard — e.g. video/*
  3. Universal fallback — *

If no pattern matches, Payload renders the default built-in preview for that file.

1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: {
6
admin: {
7
components: {
8
filePreview: {
9
'application/pdf': '/components/PdfPreview#PdfPreview', // exact match
10
'video/*': '/components/VideoPreview#VideoPreview', // category wildcard
11
'*': '/components/GenericPreview#GenericPreview', // universal fallback
12
},
13
},
14
},
15
},
16
fields: [],
17
}

Building a File Preview component

A File Preview component can be a Server or Client Component. It receives the following props describing the current file:

Prop

Type

Description

fileSrc

string

Resolved URL of the file (doc.url)

filename

string

The file's name

filesize

number

The file's size in bytes

mimeType

string

The file's MIME type

width

number

Image width, when available

height

number

Image height, when available

1
'use client'
2
import type { UploadFilePreviewClientProps } from 'payload'
3
4
import React from 'react'
5
6
export const MediaPreview: React.FC<UploadFilePreviewClientProps> = ({
7
filename,
8
fileSrc,
9
mimeType,
10
}) => {
11
if (mimeType?.startsWith('video/')) {
12
return <video controls src={fileSrc} />
13
}
14
15
return <img alt={filename} src={fileSrc} />
16
}

Component paths use the same syntax as other Custom Components — a string with #ExportName, or an object with path and exportName.

Customizing the Upload UI

You can completely customize the upload interface in the Admin Panel by swapping in your own React components. This allows you to modify how files are uploaded, add custom fields, integrate custom actions, or enhance the upload experience.

Upload Component Configuration

To customize the upload UI for an upload-enabled collection, use the admin.components.edit.Upload property in your Collection Config:

1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: true,
6
admin: {
7
components: {
8
edit: {
9
Upload: '/components/CustomUpload#CustomUploadServer',
10
},
11
},
12
},
13
fields: [
14
{
15
name: 'alt',
16
type: 'text',
17
},
18
],
19
}

Building Custom Upload Components

Custom upload components must integrate with Payload's form system to work correctly. The recommended approach is to use Payload's built-in <Upload> component from @payloadcms/ui and wrap it with additional functionality.

Basic Example

Here's a minimal example showing how to create a custom upload component:

Server Component (/components/CustomUpload.tsx):

1
import React from 'react'
2
import type {
3
PayloadServerReactComponent,
4
SanitizedCollectionConfig,
5
} from 'payload'
6
import { CustomUploadClient } from './CustomUpload.client'
7
8
export const CustomUploadServer: PayloadServerReactComponent<
9
SanitizedCollectionConfig['admin']['components']['edit']['Upload']
10
> = (props) => {
11
return (
12
<div>
13
<h2>Custom Upload Interface</h2>
14
<CustomUploadClient {...props} />
15
</div>
16
)
17
}

Client Component (/components/CustomUpload.client.tsx):

1
'use client'
2
import React from 'react'
3
import { Upload, useDocumentInfo } from '@payloadcms/ui'
4
5
export const CustomUploadClient = () => {
6
const { collectionSlug, docConfig, initialState } = useDocumentInfo()
7
8
return (
9
<Upload
10
collectionSlug={collectionSlug}
11
initialState={initialState}
12
uploadConfig={'upload' in docConfig ? docConfig.upload : undefined}
13
/>
14
)
15
}

Advanced Example with Custom Actions

You can add custom actions, drawers, and fields to enhance the upload experience:

1
'use client'
2
import React from 'react'
3
import {
4
Drawer,
5
DrawerToggler,
6
TextField,
7
Upload,
8
useDocumentInfo,
9
} from '@payloadcms/ui'
10
11
const customDrawerSlug = 'custom-upload-drawer'
12
13
const CustomDrawer = () => {
14
return (
15
<Drawer slug={customDrawerSlug}>
16
<h2>Custom Upload Options</h2>
17
<TextField
18
field={{
19
name: 'customField',
20
label: 'Custom Field',
21
type: 'text',
22
}}
23
path="customField"
24
/>
25
</Drawer>
26
)
27
}
28
29
const CustomDrawerToggler = () => {
30
return (
31
<DrawerToggler slug={customDrawerSlug}>
32
<button type="button">Open Custom Options</button>
33
</DrawerToggler>
34
)
35
}
36
37
export const CustomUploadClient = () => {
38
const { collectionSlug, docConfig, initialState } = useDocumentInfo()
39
40
return (
41
<div>
42
<CustomDrawer />
43
<Upload
44
collectionSlug={collectionSlug}
45
customActions={[<CustomDrawerToggler key="custom-drawer" />]}
46
initialState={initialState}
47
uploadConfig={'upload' in docConfig ? docConfig.upload : undefined}
48
/>
49
</div>
50
)
51
}

Available Hooks and Components

When building custom upload components, you have access to several useful hooks and components from @payloadcms/ui:

Hook / Component

Description

useDocumentInfo()

Get collection slug, document config, and initial state

useField()

Access and manipulate form field state

useBulkUpload()

Access bulk upload context

<Upload>

Main upload component with file selection, drag-and-drop, and preview

<Drawer>

Modal drawer for additional UI

<DrawerToggler>

Button to open/close drawers

<TextField>, etc.

Form field components

Custom Upload Fields vs. Custom Upload Collections

It's important to understand the difference between these two customization approaches:

Approach

Configuration

Use Case

Upload Collection Customization

admin.components.edit.Upload

Customize the UI when editing documents in an upload-enabled collection (e.g., the Media collection edit view)

Upload Field Customization

admin.components.Field on an upload field

Customize the field that references uploads in other collections (e.g., a "Featured Image" field on a Posts collection)

Example of Upload Field Customization:

1
import type { CollectionConfig } from 'payload'
2
3
export const Posts: CollectionConfig = {
4
slug: 'posts',
5
fields: [
6
{
7
name: 'featuredImage',
8
type: 'upload',
9
relationTo: 'media',
10
admin: {
11
components: {
12
Field: '/components/CustomUploadField',
13
},
14
},
15
},
16
],
17
}

For more details on customizing fields, see Field Components.

Component Export Syntax

Custom components are referenced using file paths. Both default exports and named exports are supported:

1
// Named export with hash syntax
2
Upload: '/components/CustomUpload#CustomUploadServer'
3
4
// Default export (no hash needed)
5
Upload: '/components/CustomUpload'
6
7
// Alternative: using exportName property
8
Upload: {
9
path: '/components/CustomUpload',
10
exportName: 'CustomUploadServer',
11
}

For more details on component paths, see Custom Components.

Restricted File Types

Possibly problematic file types are automatically restricted from being uploaded to your application. If your Collection has defined mimeTypes or has set allowRestrictedFileTypes to true, restricted file verification will be skipped.

Restricted file types and extensions:

File Extensions

MIME Type

exe, dll

application/x-msdownload

exe, com, app, action

application/x-executable

bat, cmd

application/x-msdos-program

exe, com

application/x-ms-dos-executable

dmg

application/x-apple-diskimage

deb

application/x-debian-package

rpm

application/x-redhat-package-manager

exe, dll

application/vnd.microsoft.portable-executable

msi

application/x-msi

jar, ear, war

application/java-archive

desktop

application/x-desktop

cpl

application/x-cpl

lnk

application/x-ms-shortcut

pkg

application/x-apple-installer

htm, html, shtml, xhtml

text/html

php, phtml

application/x-httpd-php

js, jse

text/javascript

jsp

application/x-jsp

py

text/x-python

rb

text/x-ruby

pl

text/x-perl

ps1, psc1, psd1, psh, psm1

application/x-powershell

vbe, vbs

application/x-vbscript

ws, wsc, wsf, wsh

application/x-ms-wsh

scr

application/x-msdownload

asp, aspx

application/x-asp

hta

application/x-hta

reg

application/x-registry

url

application/x-url

workflow

application/x-workflow

command

application/x-command

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:

1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: {
6
mimeTypes: ['image/*', 'application/pdf'],
7
},
8
}

Uploading Files

To upload a file, use your collection's create 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 if possible.

1
const fileInput = document.querySelector('#your-file-input')
2
const formData = new FormData()
3
4
formData.append('file', fileInput.files[0])
5
6
// Replace with the fields defined in your upload-enabled collection.
7
// The example below includes an optional field like 'title'.
8
formData.append(
9
'_payload',
10
JSON.stringify({
11
title: 'Example Title',
12
description: 'An optional description for the file',
13
}),
14
)
15
16
fetch('api/:upload-slug', {
17
method: 'POST',
18
body: formData,
19
/**
20
* Do not manually add the Content-Type Header
21
* the browser will handle this.
22
*
23
* headers: {
24
* 'Content-Type': 'multipart/form-data'
25
* }
26
*/
27
})

MCP uploads

The MCP plugin's createDocument and updateDocument tools accept files from a URL or as base64 data. Call getCollectionSchema first and use only the sources listed in upload.sources.

MCP does not expose the Local API's filePath option, preventing MCP clients from requesting arbitrary files from the server's filesystem.

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.

1
const localFilePath = path.resolve(__dirname, filename)
2
3
await payload.create({
4
collection: 'media',
5
overrideAccess: true,
6
data: {
7
alt,
8
},
9
filePath: localFilePath,
10
})

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

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:

1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: {
6
pasteURL: {
7
allowList: [
8
{
9
hostname: 'payloadcms.com', // required
10
pathname: '',
11
port: '',
12
protocol: 'https',
13
search: '',
14
},
15
{
16
hostname: 'example.com',
17
pathname: '/images/*',
18
},
19
],
20
},
21
},
22
}

You can also adjust server-side fetching at the upload level as well, this does not effect the CORS policy like the pasteURL option does, but it allows you to skip the safe fetch check for specific URLs.

1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: {
6
skipSafeFetch: [
7
{
8
hostname: 'example.com',
9
pathname: '/images/*',
10
},
11
],
12
},
13
}
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 function from the Collection itself. You can use this to control who should be allowed to see your uploads, and who should not.

If any file transformer is configured, requests for a dynamically-transformed variant of a file (for example, a resized image) run this same read access control, with req.fileTransform set to true for the duration of the transform-aware call. access.read can run a second time, without the flag, to decide an ordinary read — see Access control for details.

Modifying response headers

You can modify the response headers for files by specifying the modifyResponseHeaders option in your upload config. This option accepts an object with existing headers and allows you to manipulate the response headers for media files.

Modifying existing headers

With this method you can directly interface with the Headers object and modify the existing headers to append or remove headers.

1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: {
6
modifyResponseHeaders: ({ headers }) => {
7
headers.set('X-Frame-Options', 'DENY') // You can directly set headers without returning
8
},
9
},
10
}

Return new headers

You can also return a new Headers object with the modified headers. This is useful if you want to set new headers or remove existing ones.

1
import type { CollectionConfig } from 'payload'
2
3
export const Media: CollectionConfig = {
4
slug: 'media',
5
upload: {
6
modifyResponseHeaders: ({ headers }) => {
7
const newHeaders = new Headers(headers) // Copy existing headers
8
newHeaders.set('X-Frame-Options', 'DENY') // Set new header
9
10
return newHeaders
11
},
12
},
13
}

Was this page helpful?

Next

Storage Adapters