Skip to content
Want deep learning about Payload? Payload Essentials is for you!Enroll Now

Payload Collections: Build Your First Collection

PayloadPayload CMS7:36 video • ~7 min readFree

In this video we build collections from scratch.

You've got Payload CMS running, and you've got an admin panel with basically nothing in it. Let's fix that. A collection is where your content lives, and by the end of this tutorial you'll have a collection you can log into, edit, and control who can access.

This builds on the setup post, where we install Payload and go through the config file. If you don't have a Payload project running, or you don't know where to start, start there and come back to this one.

The exact version I'm on is 3.88.0. Payload 4 is on the way as I'm writing this, so expect an update shortly after that releases.

What is a collection in Payload CMS?

A collection is a group of documents that share the same structure. Some examples include posts, pages, products, and users. You can have as many as you need, and each one gets its own card in your admin panel.

When you define a collection, you automatically get three ways to read it: the Local API, a REST API, and a GraphQL API. You don't write any of them. You can see the JSON for any single document in its API tab in the admin panel.

How to create your first collection

To create a collection, I'll build a posts collection. Create a folder called collections, and inside it a file called Posts.ts. You don't have to organise it this way, but a collection per file is what keeps your file structure readable.

In Posts.ts, import the CollectionConfig type from payload. Then export a constant (I'll call mine Posts) and assign it the CollectionConfig type. It needs a slug, and a fields array, which will just have our title text field for now.

Posts.ts
1import type { CollectionConfig } from 'payload'
2
3export const Posts: CollectionConfig = {
4 slug: 'posts',
5 fields: [{ name: 'title', type: 'text' }],
6}

You'll want to keep the slug the same as the collection name.

The fields array is what your documents actually hold. I've put one text field in for now. We'll talk about fields in more detail in the next post.

Now you can open up your payload.config.ts and import it into the collections array.

payload.config.ts
1import { Posts } from '@/collections/Posts'
2
3// inside buildConfig
4collections: [Users, Media, Posts],

Save that, and Posts shows up in your admin UI.

Admin options: how your collection looks in the admin panel

Everything from here on is optional, but these options can help make your collections more useful. The first group is admin, which controls how the collection behaves in the admin panel.

useAsTitle is a common admin option. It tells Payload which field labels the document everywhere it's used, like in the list view, in the breadcrumb, and inside any relationship field using this collection.

Without it, every row in the list view is an ID. That's the default, and it's the single most common thing people complain about with their first Payload project. Set useAsTitle to title and that same list becomes readable.

There is one catch. It has to be a top-level field. You can't use a group or an array for it, nor can you use a virtual field.

Next is defaultColumns, which sets what the list view opens with. That way you decide what an editor sees first, instead of taking whatever order the fields happen to be in.

group puts related collections under one heading in your sidebar and admin homepage. If you've got categories and tags and topics, group them under something like Meta and your sidebar becomes more easily accessible.

If you set group to false the collection disappears from the navigation entirely, but the API still works. That's useful for a collection your editors never need to open.

description puts a line of help text under the collection title. It's a small thing, but it can be helpful when you have a lot of similar collections.

The last one I'll talk about is listSearchableFields, which controls what the search box in the list view searches. Be sure to set it if you want to change the default behavior.

1admin: {
2 useAsTitle: 'title',
3 defaultColumns: ['title', 'createdAt'],
4 group: 'Content',
5 description: 'Blog posts, shown newest first.',
6 listSearchableFields: ['title'],
7},

Access control: who can do what

The access option is where you decide who can do what in a collection.

There are five basic functions: read, create, update, delete, and admin. Each one can return true, false, or a query.

read returning true means anybody can read it. false means nobody can read it.

The query version is where this gets powerful. Instead of answering yes or no, you return a filter, and Payload applies it to every request.

So let's say posts should only be readable by the author who wrote them. We'll add the access option, and within the object, assign a function to read. The function takes some arguments, which we can use to pull out our request, which holds our user. If there's no user, we return false. Otherwise, we return a query that returns documents only if the user's id is equal to the author's id.

1access: {
2 read: ({ req: { user } }) => {
3 if (!user) return false
4 return { author: { equals: user.id } }
5 },
6},

That's a where clause Payload merges into the query, so an author can't see anybody else's posts.

Posts doesn't have an author field yet. We add it as a relationship field in the fields post.

We'll talk more about roles and full role-based access control in a future tutorial.

Auth: the collection people log in with

The auth option is short. Add auth: true to a collection and it becomes the collection people log in with. That's it in its most basic form.

Your Users collection already has it, which is why Payload asked you to create a user the first time you started it up.

There's much more to it, like token expiry, email verification, lockouts after failed attempts, and API keys. Authentication gets its own tutorial, so we'll come back to those later.

Upload: turning a collection into a media collection

The upload option turns a regular collection into a media collection.

Set upload to true and you're done. The collection takes files now, and you get a file picker in the admin panel.

You'll almost always want the object form instead, though, because that's where the useful stuff is.

  • mimeTypes is a whitelist. Pass image/* and it only takes images. Pass webp and png specifically and it only takes those two. And this isn't just for images. You can restrict a collection to PDFs the same way.
  • imageSizes generates variants on upload, so one file gives you a thumbnail, a card, and a hero. This needs sharp installed. If you skipped that in the setup post, this won't work.
  • formatOptions converts files as they come in. Set the format to webp and everything that is uploaded in this collection gets converted.
  • adminThumbnail controls what you see in the dashboard. Pass a string matching one of your imageSizes and it uses that size. Or pass a function, which is what I prefer, and build the path yourself.
1adminThumbnail: ({ doc }) => `https://cdn.example.com/${doc.filename}`,

The last option we'll discuss is displayPreview, which you can set to false when you don't want a preview before the upload happens. Handy for documents, less so for images.

Uploads will need their own tutorial, but for now you've got a media collection up and running.

Three more collection options worth knowing

There are three more collection options worth knowing about.

  • defaultSort sets which field the list view sorts by, and a minus in front reverses it. This makes it easy to change the default sorting behavior in a way that makes sense for you.
  • defaultPopulate limits which fields come back when this collection gets pulled in through a relationship. Without it, every relationship populates the entire document. Set it to just the slug and the title, and you stop passing whole posts to a page that only needs a link and a label.
  • versions, with drafts turned on, lets you edit documents without publishing the document. We'll discuss this more when we get to live preview and draft mode.

Of course there's more to collection configs than this, and we'll get to some of it in future tutorials, like hooks, custom endpoints, and bulk operations.

What you've built

And that's all there is to it! You've got a real collection, an admin panel you configured, an access rule, and a media collection ready for files.

If you'd like a sequential set of videos covering a full Payload setup, my course Payload Essentials goes further: https://nlvcodes.com/courses/payload-essentials

Keep going