Trendy Coder Logo
  • Home
  • About
  • Blog
  • Newsletter

Uploading Made Easy: Exploring Media Features in Payload CMS

Posted on: September 23 2025
By Dave Becker
Hero image for Uploading Made Easy: Exploring Media Features in Payload CMS

Managing media files is a critical part of any modern content platform, and Payload CMS makes it easier than ever. In this article, we’ll take a closer look at how Payload handles file uploads and media management out of the box.

From configuring upload fields to organizing assets in collections, I’ll walk through the core features and show you how to seamlessly integrate media into your content workflows.

In this article, I'll be covering:

  • What is the upload field?
  • Why are upload fields important?
  • How to use upload fields.
  • Relationships to upload fields.
  • Querying on upload fields.

Whether you're building a Headless CMS stack or just exploring Payload's capabilities, this guide will help you make the most of its powerful media tools.

What is the upload field?

If you've ever tried to build your own upload capable application, then you know how complex it can be. Let's find out how simple Payload CMS makes this process using their built-in upload field.

The upload field in Payload CMS can be added to any Collection to allow content authors to upload and attach files to a given Collection that is configured for upload support.

For example, by simply setting the upload field to true enables file upload support.

Collection with upload support
import type { CollectionConfig } from 'payload';

export const MyPictures: CollectionConfig = {
  slug: 'mypictures',
  
  // indicates the Collection should support uploads
  upload: true,
};

Pretty easy right?

In order to upload and store our media files in the Collection, we can simply use the upload field and point to any Collection that has upload support set to true.

Simple upload field
export const Cars: CollectionConfig = {
  slug: 'somecollection',
  ...
  fields: [
    {
      name: 'pictures',
      type: 'upload',
      relationTo: 'mypictures',
      // indicates if multiple files can be uploaded
      hasMany: true,
    }
  ]
  ...

Why are upload fields important?

The upload field is probably one of the most important fields when building any Headless CMS application. With very little effort, it immediately provideds the following capabilities:

  • Upload media files to any designated Collection.
  • Can upload media files locally or to cloud using adapter plugins.
  • Automatically creates consistent TypeScript interfaces.
  • Easily manage uploaded content through the admin panel.
  • Control access to content.
  • Image resizing and dimensions for various devices.

Payload's media upload capabilities greatly streamline upload support and allows developers to focus on building apps rather than boilerplate infrastructure.

How to use upload fields

As we saw earlier in the examples, the upload support requires two things:

  • A Collection marked with upload support.
  • An upload field that points to the target upload destination using the relationTo property.

Defining an upload Collection

So let's take a look at a more realistic example.

I'll define some Collections to simulate an online used cars website that let's users upload images of cars they are selling.

For example:

  • Pictures: The media enabled Collection.
  • Cars: The Collection to add car details and images that will point to the Pictures media destination.

Create the upload Collection

Here's the pictures Collection to manage uploads.

Pictures Collection for cars
import type { CollectionConfig } from 'payload';

export const Pictures: CollectionConfig = {
  slug: 'pictures',
  access: {
    // setting read access to public for now
    read: () => true,
  },
  upload: true,
};

Create the upload field

Next I'll add the cars Collection that defines the upload field, in addition to other fields to hold details about a car.

The upload field consists of:

  • name: the name of the field in the data.
  • type: type is set to upload and will provide the upload form field.
  • relationTo: Points to the pictures Collection.
  • hasMany: Is set to true to allow more than photo to be uploaded.
Cars interface for adding details and images
import type { CollectionConfig } from 'payload';

export const Cars: CollectionConfig = {
  slug: 'cars',
  admin: {
    useAsTitle: 'model',
  },
  access: {
    // set to public access for now
    read: () => true,
  },
  fields: [
    // upload field
    {
      name: 'pictures',
      type: 'upload',
      relationTo: 'pictures',
      hasMany: true,
    },
    {
      name: 'make',
      label: 'Make',
      type: 'text',
    },
    {
      name: 'model',
      label: 'Model',
      type: 'text',
    },
    {
      name: 'year',
      label: 'Year',
      type: 'number',
    },
    {
      name: 'mileage',
      label: 'Mileage',
      type: 'number',
    },
    {
      name: 'price',
      label: 'Price',
      type: 'number',
    },
    {
      name: 'color',
      label: 'Color',
      type: 'text',
    },
    {
      name: 'notes',
      type: 'richText',
    },
  ],
};

And lastly, we just need to add the new Collections to the main payload.config.js.

export default buildConfig({
  //...
  collections: [
    // other default collections here i.e. such as Users and Media
    Cars, 
    Pictures
  ],
});

Uploading media in the Admin UI

The Admin UI will automatically be generated to manage the cars and pictures Collections.

The Cars edit form

Here's the cars edit form with the upload field to select photos.

A Payload CMS admin form to upload an image and car details.

The upload field chooser

The field will have the option to upload new images or select from the existing uploaded file content.

A Payload CMS form field to select an image to be uploaded.

The Pictures repository

Once the photos are uploaded you'll be able to see them in the listing page.

A Payload CMS admin UI table list of uploaded car pictures.

The upload field relationship

One thing to note is that the upload field is a special relationship field, since it requires a relationTo value to be set.

So it's very similar to the relationship with some small differences.

{
  //...
  fields: [
    {
      name: 'pictures',
      type: 'upload',
      // sets a relation to pictures
      relationTo: 'pictures',
      hasMany: true,
    },
  ];
}

Displaying a Car's pictures

In order to view the uploaded images, we'll need a React page to do the following:

  • Makes a query for a car's make and model.
  • Lists any car data associated from the query.
  • Iterate through the pictures array and display each picture using a some grid CSS.

Building a details page with Next.js

Here's a simple server-side rendered Next.js page to query the car data and display the car's pictures.

For the UI, I'm using the following:

  • Tailwind CSS utility classes for styling.
  • Using Payload @payloadcms/richtext-lexical package for deserializing the saved rich text content, which is saved in AST format in the database.
  • A query helper qs-esm to stringify queries.
import React from 'react';
import { stringify } from 'qs-esm';
import { Car as CarType } from '@payload-types';
import { Where } from 'payload';
import { RichText } from '@payloadcms/richtext-lexical/react';
import { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical';

const query: Where = {
  make: {
    equals: 'Ford LTD',
  },
  and: [
    {
      model: {
        equals: 'Country Squire',
      },
    },
  ],
};

export default async function Page() {
  const stringifiedQuery = stringify(query, { addQueryPrefix: true });
  const res = await fetch(`http://localhost:3000/api/cars${stringifiedQuery}`, {
    method: 'GET',
  });
  const data = await res.json();

  const car: CarType = data?.docs?.[0];

  const { model, price, make, year, mileage, pictures, notes } = car;

  return (
    <div className="flex-col justify-evenly p-5">
      <h1>For Sale</h1>
      <div className="flex-col">
        <div className="grid grid-cols-2 gap-3 w-1/2 mb-5">
          <div className="col">Model</div>
          <div className="col">{model}</div>
          <div className="col">Make</div>
          <div className="col">{make}</div>
          <div className="col">Year</div>
          <div className="col">{year}</div>
          <div className="col">Mileage</div>
          <div className="col">{mileage}</div>
          <div className="col">Price</div>
          <div className="col">{price}</div>
        </div>
        <div>
          <RichText data={notes as SerializedEditorState} />
        </div>
      </div>
      <div className="grid grid-cols-2">
        {pictures &&
          pictures.map((picture) => {
            if (picture && typeof picture === 'object') {
              const { url } = picture;
              return <img src={`http://localhost:3000${url}`}/>;
            }
            return null;
          })}
      </div>
    </div>
  );
}

Query response data

Here's an abbreviated version of the query response with the omission of the nested notes and date fields:

  • The pictures field key is returned as an array since it was set to upload many.
  • Each pictures array item provides the essential image info for optimal rendering.
{
  "docs": [
    {
      "make": "Ford LTD ",
      "model": "Country Squire",
      "mileage": 200000,
      "color": "Metalic Pea",
      "year": 1979,
      "notes": {
        // omitted for brevity
      },
      "pictures": [
        {
          "filename": "ford-ltd-79-country-squire.png",
          "mimeType": "image/png",
          "filesize": 1158455,
          "width": 1578,
          "height": 884,
          "focalX": 50,
          "focalY": 50,
          "id": "67f8356b290d04d676c4f5be",
          "url": "/api/pictures/file/ford-ltd-79-country-squire.png",
          "thumbnailURL": null
        }
      ],
      "price": 2000,
      "id": "67f8280f290d04d676c4ec5a"
    }
  ]
}

Viewing the car details page

Here's the rendered car details page content showing the upload pictures below the car information.

Shows a web page displaying some car details and images in a simple for sale Ad.

In Conclusion

Even though this article just touched the surface of what Payload's media Collections can do, it gives a good idea of how things come together end to end.

Building any SaaS app these days will always require the capbility to upload content in some way. The Payload framework provides extensive support for these types of operations in a consistent way.

Here's some key benefits of Payload Collection media support:

  • Media content doesn't need to live in one large bucket, it can be broken up into more manageable media Collections.
  • Each media Collection can directly store its content to the cloud using adapters to each cloud platform.
  • Upload Collections were designed to directly serve resources to users using access control or token access.
  • Payload upload capabilities support multi-tenant capabilities allowing tenants to manage their own content.
  • By leveraging upload Collections effectively it can immensely speed up your development efforts.

I hope this article has helped to scratch the surface using Payload CMS media and uploading features.

Topics

SEOLinuxSecuritySSHEmail MarketingMore posts...

Related Posts

Hero image for Boost Payload CMS with Search: Step-by-Step Tutorial
Posted on: August 11 2025
By Dave Becker
Boost Payload CMS with Search: Step-by-Step Tutorial
Hero image for Create Custom Forms Using Payload CMS Form Builder Plugin
Posted on: August 11 2025
By Dave Becker
Create Custom Forms Using Payload CMS Form Builder Plugin
Hero image for Document Nesting With Payload's Nested Docs Plugin
Posted on: April 04 2025
By Dave Becker
Document Nesting With Payload's Nested Docs Plugin
Hero image for Exploring the Game-Changing Features of Payload CMS 3.0
Posted on: April 04 2025
By Dave Becker
Exploring the Game-Changing Features of Payload CMS 3.0
Hero image for GraphQL Optimization in Payload CMS
Posted on: April 04 2025
By Dave Becker
GraphQL Optimization in Payload CMS
Hero image for Maximizing Efficiency: The Power of Payload CMS Blocks
Posted on: August 11 2025
By Dave Becker
Maximizing Efficiency: The Power of Payload CMS Blocks
Hero image for Payload CMS Collections: How They Streamline Content Management
Posted on: April 04 2025
By Dave Becker
Payload CMS Collections: How They Streamline Content Management
Hero image for Payload CMS: Getting Started Using the New Join Field
Posted on: August 11 2025
By Dave Becker
Payload CMS: Getting Started Using the New Join Field
Hero image for Payload CMS SEO Plugin: Boosting Your Site's Search Ranking
Posted on: April 04 2025
By Dave Becker
Payload CMS SEO Plugin: Boosting Your Site's Search Ranking
Hero image for Server-Side Pagination Made Easy in Payload CMS
Posted on: August 11 2025
By Dave Becker
Server-Side Pagination Made Easy in Payload CMS
Trendy Coder Logo
Resources
  • Blog
Website
  • Home
  • About us
Subscribe

Get the latest news and articles to your inbox periodically.

We respect your email privacy

© TrendyCoder.com. All rights reserved.