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 set up and customize Collections

Community Guide
AuthorNick Vogel

In this guide, we'll walk through Collections in Payload, how to use them to structure your project’s data, workflows, and scale efficiently.

In Payload, a Collection is a group of documents that share a common structure. You can have as many collections as you need for a project, and each collection you create will take up its own space in your admin dashboard.

Each document you create is stored in your database using the fields you define.

What you define in each collection automatically generates Local, REST, and GraphQL APIs for you to query your database and render data on your frontend. These APIs are represented as JSON in the API window in each of your individual collection item’s view.

It's best practice to create your collections in a separate file and then import them into your config.

Defining a Collection

To define a collection, first, create a new file for your collection. I typically store these collection files in a collections directory, but you don’t need to do that. If I want to create a new Posts collection to store my blog posts, I create a new directory inside /collections/ called Posts and then add a TypeScript file called config.ts.

The structure would look as follows:

1
/collections
2
└── /Posts
3
└── config.ts

There are a few things you need to do to get your collection going.

First, you need to import the CollectionConfig type from Payload. This ensures you're working within the structure Payload expects.

1
import { CollectionConfig } from 'payload'

Next, export a constant named after the collection, in this case, Posts, and assign it the CollectionConfig type.

1
export const Posts: CollectionConfig = {
2
slug: 'posts',
3
fields: [
4
{
5
name: 'title',
6
type: 'text',
7
required: true,
8
},
9
{
10
name: 'slug',
11
type: 'text',
12
},
13
{
14
name: 'content',
15
type: 'richText',
16
},
17
],
18
};

The slug can be whatever you want, but it's recommended to keep it the same as the collection name to prevent confusion.

Once you've created the collection, you can import it into your Payload configuration:

1
import { Posts } from './collections/Posts';
2
3
export const config = {
4
collections: [Users, Media Posts],
5
};

Admin Customization

Collections, just like fields, have various ways to configure how they appear in the admin panel.

Grouping Collections

The group option allows you to organize collections in the sidebar by grouping related collections together.

1
export const Posts: CollectionConfig = {
2
slug: 'posts',
3
admin: {
4
group: 'posts' // Sorting Posts into their own group
5
},
6
fields: [
7
{
8
name: 'title',
9
type: 'text',
10
},
11
{
12
name: 'slug',
13
type: 'text'
14
},
15
{
16
name: 'content',
17
type: 'richText'
18
}
19
]
20
};

You can also set group: false to hide it from the navigation while still making it accessible via the API.

Using a Field as a Title

The useAsTitle option determines which field should be used as the title for each document in the admin panel. For example, useAsTitle: 'name'.

1
export const Posts: CollectionConfig = {
2
slug: 'posts',
3
admin: {
4
group: 'posts'
5
useAsTitle: 'title' // Set how the document is titled throughout the admin panel
6
},
7
fields: [
8
{
9
name: 'title',
10
type: 'text',
11
},
12
{
13
name: 'slug',
14
type: 'text'
15
},
16
{
17
name: 'content',
18
type: 'richText'
19
}
20
]
21
};

If useAsTitle is not set, Payload defaults to displaying the document's id.

Adding a Collection Description

The description option allows you to provide extra context under the collection label in the admin panel.

1
export const Posts: CollectionConfig = {
2
slug: 'posts',
3
admin: {
4
group: 'posts'
5
useAsTitle: 'title'
6
description: 'This is a blog collection' // A description to define within admin panel
7
},
8
fields: [
9
{
10
name: 'title',
11
type: 'text',
12
},
13
{
14
name: 'slug',
15
type: 'text'
16
},
17
{
18
name: 'content',
19
type: 'richText'
20
}
21
]
22
};

Access Control

You can fine-tune access control to specify who can read, create, update, or delete documents.

1
export const Posts: CollectionConfig = {
2
slug: 'posts',
3
admin: {
4
group: 'posts'
5
useAsTitle: 'title'
6
description: 'This is a blog collection' // A description to define within admin panel
7
},
8
access: {
9
read: () => true,
10
create: () => true,
11
update: () => true, // means anyone can read, create, or update in posts collection
12
}
13
fields: [
14
{
15
name: 'title',
16
type: 'text',
17
},
18
{
19
name: 'slug',
20
type: 'text'
21
},
22
{
23
name: 'content',
24
type: 'richText'
25
}
26
]
27
};

Default Sorting

To define how documents should be sorted in the admin panel by default, use defaultSort:

1
export const Posts: CollectionConfig = {
2
slug: 'posts',
3
admin: {
4
group: 'posts'
5
useAsTitle: 'title'
6
description: 'This is a blog collection'
7
},
8
access: {
9
read: () => true,
10
create: () => true,
11
update: () => true,
12
}
13
defaultSort: '[title]', // This will sort title of posts by Ascending
14
fields: [
15
{
16
name: 'title',
17
type: 'text',
18
},
19
{
20
name: 'slug',
21
type: 'text'
22
},
23
{
24
name: 'content',
25
type: 'richText'
26
}
27
]
28
};

Labels

If you want to display a different name in the admin panel than the slug, use Labels.

A great use case for this is with our Posts example.

If we want to query this collection using our posts slug but we want to call these Blogs in your admin dashboard, we can do that by setting the label option to an object with a plural and singular option.

These are case sensitive, so be sure that you type these in exactly like you want to see them in the dashboard.

1
export const Posts: CollectionConfig = {
2
slug: 'posts',
3
admin: {
4
group: 'posts'
5
useAsTitle: 'title'
6
description: 'This is a blog collection'
7
},
8
access: {
9
read: () => true,
10
create: () => true,
11
update: () => true,
12
}
13
defaultSort: '[title]',
14
labels: {
15
singular: 'Blog Post',
16
plural: 'Blog Posts',
17
fields: [
18
{
19
name: 'title',
20
type: 'text',
21
},
22
{
23
name: 'slug',
24
type: 'text'
25
},
26
{
27
name: 'content',
28
type: 'richText'
29
}
30
]
31
};

Uploads

If you want your collection to support file uploads, you'll need to include either upload: true or set upload with an object with some more options. This is extremely flexible and can be used to convert files on upload, restrict accepted MIME types, and allows you to handle how thumbnails are presented.

In the Media collection, you'll see that upload is set to true. If you want more control than that, you can just open up an empty object and start with formatOptions. When you upload files, you can use format options to automatically convert files into other formats.

If you want all of your images to be converted to webp, for example, you can do that by including format: 'webp' and it will convert anything that you upload to webp by default.

For MIME types, you can set mimeTypes option and specify what types of documents your upload field allows. This expects an array of strings that acts as a whitelist of available types. For example, if you want to only include images, you can use mimeTypes: ['image/*'] which would allw the user to upload any type of image.

If you only want webp and PNG images allowed, you would use mimeTypes: ['image/png', 'image/webp' ].

This is not limited to images either. You can set restrictions to allow PDFs or other documents—all by just setting what MIME types to accept.

1
export const Media: CollectionConfig = {
2
slug: 'media',
3
access: {
4
read: (): boolean => true,
5
},
6
fields: [
7
{
8
name: 'alt',
9
type: 'text',
10
required: true,
11
},
12
],
13
upload: {
14
formatOptions: {
15
format: 'webp',
16
},
17
},
18
mimeTypes: ['image/png', 'image/webp']
19
};

Admin Thumbnails

You can also customize how images appear as thumbnails in the admin panel.

You can either use a string to tell your collection how to handle the thumbnail, or set it as a function.

When you use a string, you can use something like, adminThumbnail: 'small' but will only work if you include the imageSizes.

1
export const Media: CollectionConfig = {
2
slug: 'media',
3
access: {
4
read: (): boolean => true,
5
},
6
fields: [
7
{
8
name: 'alt',
9
type: 'text',
10
required: true,
11
},
12
],
13
upload: {
14
formatOptions: {
15
format: 'webp',
16
},
17
imageSizes: [
18
{
19
name: 'small',
20
width: 1200,
21
height: 600,
22
},
23
],
24
},
25
mimeTypes: ['image/png', 'image/webp'],
26
adminThumbnail: 'small',
27
};

I prefer to use a function, which takes the document as an argument. You could use your own media bucket and programmatically create a path to the file to then be used in your admin dashboard.

1
mimeTypes: ['image/png', 'image/webp'],
2
adminThumbnail: ({ doc: Record<string, ?> }): string => `https://google.com/path/to/file/${doc.filename}`,

What this does is doing is reviewing the document, which in this case is a media file, and it's going to take the file name and apply it to the end of the string of whatever file path you're using.

We don't have a media bucket set up for this example, so the current path won't return anything for us.

Optimizing Data Queries

By default, relationship and upload fields return the entire document, but you can limit this using defaultPopulate.

For example, if you only want to send the slug and name field from the users collection, you can set defaultPopulate to include slug and name as true.

1
import { CollectionConfig } from 'payload';
2
3
export const Users: CollectionConfig = {
4
slug: 'users',
5
auth: true,
6
admin: {
7
useAsTitle: 'email',
8
},
9
defaultPopulate: {
10
slug: true,
11
name: 'users',
12
},
13
fields: [
14
{
15
name: 'active',
16
type: 'checkbox',
17
defaultValue: false,
18
},
19
{
20
name: 'slug',
21
type: 'text',
22
},
23
{
24
type: 'richText',
25
name: 'test',
26
editor: 'lexical',
27
},
28
],
29
};
30
31
export default Users;

And then we would add a relationship field.

1
export const Posts: CollectionConfig = {
2
slug: 'posts',
3
admin: {
4
group: 'posts',
5
useAsTitle: 'title',
6
description: 'This is a blog collection',
7
},
8
access: {
9
read: () => true,
10
create: () => true,
11
update: () => true,
12
},
13
defaultSort: '[title]',
14
labels: {
15
singular: 'Blog Post',
16
plural: 'Blog Posts',
17
},
18
fields: [
19
{
20
name: 'title',
21
type: 'text',
22
},
23
{
24
name: 'slug',
25
type: 'text',
26
},
27
{
28
name: 'content',
29
type: 'richText',
30
},
31
{
32
name: 'number',
33
type: 'number',
34
},
35
{
36
name: 'users', type: 'relationship', relationTo: 'users' }
37
],
38
};

This will ensure that only those fields are populated when using that relationships with another collection.

Conclusion

Collections are the foundation of any Payload project. Whether you’re creating pages, blog posts, media uploads, user authentication, or any other content structure, understanding collection configuration is essential.

When creating a collection, keep your final design in mind so you know exactly which fields and options you need.

These configurations allow you to fully customize how your data is structured and displayed in Payload.