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.

Performance

Payload is designed with performance in mind, but its customizability means that there are many ways to configure your app that can impact performance.

With this in mind, Payload provides several options and best practices to help you optimize your app's specific performance needs. This includes the database, APIs, and Admin Panel.

Whether you're building an app or troubleshooting an existing one, follow these guidelines to ensure that it runs as quickly and efficiently as possible.

Building your application

Database proximity

The proximity of your database to your server can significantly impact performance. Ensure that your database is hosted in the same region as your server to minimize latency and improve response times.

Indexing your fields

If a particular field is queried often, build an Index for that field to produce faster queries.

When your query runs, the database will not search the entire document to find that one field, but will instead use the index to quickly locate the data.

To learn more, see the Indexes docs.

Querying your data

There are several ways to optimize your Queries. Many of these options directly impact overall database overhead, response sizes, and/or computational load and can significantly improve performance.

When building queries, combine as many of these options together as possible. This will ensure your queries are as efficient as they can be.

To learn more, see the Query Performance docs.

Optimizing your APIs

When querying data through Payload APIs, the request lifecycle includes running hooks, access control, validations, and other operations that can add significant overhead to the request.

To optimize your APIs, any custom logic should be as efficient as possible. This includes writing lightweight hooks, preventing memory leaks, offloading long-running tasks, and optimizing custom validations.

To learn more, see the Hooks Performance docs.

Writing efficient validations

If your validation functions are asynchronous or computationally heavy, ensure they only run when necessary.

To learn more, see the Validation Performance docs.

Optimizing custom components

When building custom components in the Admin Panel, ensure that they are as efficient as possible. This includes using React best practices such as memoization, lazy loading, and avoiding unnecessary re-renders.

To learn more, see the Custom Components Performance docs.

Other Best Practices

Block references

Use Block References to share the same block across multiple fields without bloating the config. This will reduce the number of fields to traverse when processing permissions, etc. and can can significantly reduce the amount of data sent from the server to the client in the Admin Panel.

For example, if you have a block that is used in multiple fields, you can define it once and reference it in each field.

To do this, use the blockReferences option in your blocks field:

1
import { buildConfig } from 'payload'
2
3
const config = buildConfig({
4
// ...
5
blocks: [
6
{
7
slug: 'TextBlock',
8
fields: [
9
{
10
name: 'text',
11
type: 'text',
12
},
13
],
14
},
15
],
16
collections: [
17
{
18
slug: 'posts',
19
fields: [
20
{
21
name: 'content',
22
type: 'blocks',
23
blockReferences: ['TextBlock'],
24
blocks: [], // Required to be empty, for compatibility reasons
25
},
26
],
27
},
28
{
29
slug: 'pages',
30
fields: [
31
{
32
name: 'content',
33
type: 'blocks',
34
blockReferences: ['TextBlock'],
35
blocks: [], // Required to be empty, for compatibility reasons
36
},
37
],
38
},
39
],
40
})

Using the cached Payload instance

Ensure that you do not instantiate Payload unnecessarily. Instead, Payload provides a caching mechanism to reuse the same instance across your app.

To do this, use the getPayload function to get the cached instance of Payload:

1
import { getPayload } from 'payload'
2
import config from '@payload-config'
3
4
const myFunction = async () => {
5
const payload = await getPayload({ config })
6
7
// use payload here
8
}

When to make direct-to-db calls

Making direct database calls can significantly improve performance by bypassing much of the request lifecycle such as hooks, validations, and other overhead associated with Payload APIs.

For example, this can be especially useful for the update operation, where Payload would otherwise need to make multiple API calls to fetch, update, and fetch again. Making a direct database call can reduce this to a single operation.

To do this, use the payload.db methods:

1
await payload.db.updateOne({
2
collection: 'posts',
3
id: post.id,
4
data: {
5
title: 'New Title',
6
},
7
})

Returning

To prevent unnecessary database computation and reduce the size of the response, you can also set returning: false in your direct database calls if you don't need the updated document returned to you.

1
await payload.db.updateOne({
2
collection: 'posts',
3
id: post.id,
4
data: { title: 'New Title' }, // See note above ^ about Postgres
5
returning: false,
6
})

Avoid bundling the entire UI library in your front-end

If your front-end imports from @payloadcms/ui, ensure that you do not bundle the entire package as this can significantly increase your bundle size.

To do this, import using the full path to the specific component you need:

1
import { Button } from '@payloadcms/ui/elements/Button'

Custom components within the Admin Panel, however, do not have this same restriction and can import directly from @payloadcms/ui:

1
import { Button } from '@payloadcms/ui'

Optimizing local development

Everything mentioned above applies to local development as well, but there are a few additional steps you can take to optimize your local development experience.

Enable Turbopack

Add --turbo to your dev script to significantly speed up your local development server start time.

1
{
2
"scripts": {
3
"dev": "next dev --turbo"
4
}
5
}

Only bundle server packages in production

By default, Next.js bundles both server and client code. However, during development, bundling certain server packages isn't necessary.

Payload has thousands of modules, slowing down compilation.

Setting this option skips bundling Payload server modules during development. Fewer files to compile means faster compilation speeds.

To do this, add the devBundleServerPackages option to withPayload in your next.config.js file:

1
const nextConfig = {
2
// your existing next config
3
}
4
5
export default withPayload(nextConfig, { devBundleServerPackages: false })