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.
AuthorNick Vogel

How to configure file storage in Payload with Vercel Blob, R2, and UploadThing

Community Guide
AuthorNick Vogel

In this tutorial, we'll walk through configuring three popular storage adapters in Payload: Vercel Blob, Cloudflare R2, and UploadThing.

Step 1: Create a Vercel Blob Storage

  1. Log in to Vercel (or create an account at vercel.com)
  2. In the top navigation bar, scroll over to Storage
  3. Click Create a Database → Select Blob
  4. Give it a name (e.g., test-storage) and click Create
  5. Under Quick Start, click on .env.local to Copy the Snippet
  6. Paste this token into your project's .env file: BLOB_READ_WRITE_TOKEN=your-token-here

Step 2: Install the Vercel Blob Storage Adapter

In your terminal, run: pnpm add @payloadcms/storage-vercel-blob

Step 3: Configure Vercel Blob in Payload

Open payload.config.ts and add the following:

1
import VercelBlobStorage from '@payloadcms/storage-vercel-blob';
2
3
// We can then go down to our plugins array, which you may or may not already have initialized. If you don't, you do need to add this plugins array to use your storage adapter.
4
5
plugins: [
6
vercelBlobStorage({
7
enabled: true,
8
collections: { // If you have another collection that supports uploads, you can add it below
9
media: true,
10
},
11
token: process.env.BLOB_READ_WRITE_TOKEN
12
})

Step 4: Match Versions and Install Dependencies

To prevent version mismatches:

  1. Open package.json
  2. Set @payloadcms/storage-vercel-blob to 3.11
  3. Run: pnpm i

Step 5: Test the Upload

  1. Start the dev server: pnpm dev
  2. Log in to Payload Admin
  3. Navigate to Media
  4. Click Create New and upload an image
  5. Verify the upload by checking Vercel Blob storage

Configuring AWS S3 Adapter (Cloudflare R2) in Payload

Step 1: Set Up Cloudflare R2 Object Storage

  1. Log in to Cloudflare (cloudflare.com)
  2. Navigate to R2 Object Storage
  3. Click Create Bucket
  4. Name it (e.g., test-storage) and select Automatic Location
  5. Click Create Bucket

Step 2: Copy API Credentials

  1. Locate the S3 API under Settings and "Bucket Details" in your bucket's dashboard
  2. Copy this into your .env file:
1
S3_ENDPOINT=your-cloudflare-endpoint
2
S3_BUCKET=test-storage

One thing that's not clear is the region you need to use. For Cloudflare’s R2, you'll use auto for your region, not what's listed as the location. This isn't true for all S3 stores though, so if you're setting this up with AWS or another S3 storage option, you'll want to see if you can find the region elsewhere.

Note: If you don't have a custom domain set up, I explain in greater detail here how to enable the R2's dev subdomain.

We still need two pieces of information: your access key ID and secret access key.

To find these, you'll return to your R2 dashboard.

  1. To find your Access Key & Secret:
  • Return to your R2 dashboard
  • Click the "API" button and then select "Manage API tokens"
  • Now click "Create API token"
  • Set permissions: Object Read/Write
  • Apply it to your test-storage bucket
  • Click "Create API token"
  • Copy the Access Key ID and Secret Access Key into your .env file
1
S3_BUCKET=test-bucket
2
S3_ACCESS_KEY_ID=your-access-key-id
3
S3_SECRET=your-secret

Step 3: Install the S3 Storage Adapter

pnpm add @payloadcms/storage-s3

Step 4: Configure Cloudflare R2 in Payload

Modify payload.config.ts:

1
import S3Storage from '@payloadcms/storage-s3';
2
3
export default {
4
plugins: [
5
S3Storage({
6
collections: {
7
media: true, // Apply storage to 'media' collection
8
},
9
bucket: process.env.S3_BUCKET || '',
10
config: {
11
credentials: {
12
accessKeyId: process.env.S3_ACCESS_KEY_ID || '',
13
secretAccessKey: process.env.S3_SECRET || '',
14
},
15
region: 'auto', // Cloudflare R2 uses 'auto' as the region
16
endpoint: process.env.S3_ENDPOINT || '',
17
},
18
}),
19
],
20
};

Step 5: Match Versions and Install Dependencies

  1. Open package.json
  2. Set @payloadcms/storage-s3 to 3.11
  3. Run: pnpm i

Step 6: Test the Upload

  1. Restart the dev server: pnpm dev
  2. Go to Media
  3. Upload an image
  4. Verify the file appears in Cloudflare R2 Storage

Configuring Uploadthing in Payload

Step 1: Set Up Uploadthing

  1. Sign up at uploadthing.com
  2. Create a New App (test-storage)
  3. Go to API Keys and copy your Uploadthing Token
  4. Add it to your .env: UPLOADTHING_TOKEN=your-token-here

Step 2: Install Uploadthing Storage Adapter

1
pnpm add @payloadcms/storage-uploadthing

As we did with the other adapters, ensure you match versions by opening package.json and setting @payloadcms/storage-uploadthingto 3.11 and run: pnpm i.

Step 3: Configure Uploadthing in Payload

Modify payload.config.ts

1
import UploadthingStorage from '@payloadcms/storage-uploadthing';
2
3
export default {
4
plugins: [
5
UploadthingStorage({
6
collections: {
7
media: true, // Apply to 'media' collection
8
},
9
options: {
10
token: process.env.UPLOADTHING_TOKEN || '',
11
acl: 'public-read' // This is optional
12
},
13
}),
14
],
15
};

Step 4: Test the Upload

  1. Restart the dev server: pnpm dev
  2. Return to Media in the Payload admin panel
  3. Upload an image
  4. Verify the file appears in Uploadthing Dashboard

Using more than one storage adapter in Payload

You can use more than one storage adapter in Payload.

For example, you might use UploadThing for documents and R2 for images. To do this, set up separate upload collections—one for each adapter.

  1. Duplicate the existing Media.ts collection and rename it to Documents.ts.
  2. Update the slug in that file to slug: 'documents'
  3. In that same collection, modify export const Media: to export const Documents:
  4. In your Payload config, import the documents collection and add it to the collections array (i.e. collections: [Users, Media, Posts, Documents])
  5. Run payload generate:types to include the new collection.
  6. In your Payload config, change the uploadthingStorage collection designation from media to documents.

Now, files in the documents collection will use UploadThing, while media files can continue using Cloudflare R2 (make sure you enable it!)—each adapter scoped to its own collection.