Payload as an alternative to Directus

Directus describes itself as an "open data platform," similar to clients like PHPMyAdmin. Payload, meanwhile, is developer-first, allowing unmatched extensibility, with modern CMS-specific features.

directus logo
unmatched extensibility in Payload
payload logo in a black box floating in the void

Building with Payload can be done quickly and effectively, thanks to its code‑based customization and developer-friendly features.

Microsoft - Payload ClientMicrosoft - Payload Client
Sowmya Reddy Peta, Engineer
Microsoft front end screenshots
BY THE NUMBERS

Payload is 3x faster than Directus

Payload vs Directus

The code for this test is available and open source. You can also read the full study.

Not just compatible with Next.js—built for it.

Being native to Next.js means that Payload delivers seamless integration and unmatched performance for modern web development.

01

Harmonious Deployment

Bring your front and back-end together in perfect harmony, simplifying deployment and enhancing collaboration across your entire stack.

02

Fully Leverage React Server Components

Extend your admin panel with server components, reducing client-side load and keeping business logic behind the scenes.

03

Turbopack Out of Box

Payload supports Turbopack from the start, accelerating development with instant updates and a brilliant developer experience.

04

Deploy Serverlessly

A Next.js foundation allows effortless deploy of your full stack to serverless platforms like Vercel, streamlining your workflow and boosting scalability.

Payload vs Directus




01

True visual editing

Payload enables users to click, point, and edit as they navigate—bypassing the need to interact with the CMS. By contrast, Directus features live preview (also available in Payload), but stops short of visual editing.

Use Payload Visual Editing to update rich text, relationships, media, and more
02

React vs Vue

Payload's Admin UI is built with React, and you can easily swap out components with your own React components. Directus is built with Vue.js, a significantly less popular alternative.

Explore Docs
1
import React from 'react'
2
import { useTranslation } from 'react-i18next'
3
4
import { getTranslation } from 'payload/utilities/getTranslation'
5
6
type Props = {
7
htmlFor?: string
8
label?: Record<string, string> | false | string
9
required?: boolean
10
}
11
12
const CustomLabel: React.FC<Props> = (props) => {
13
const { htmlFor, label, required = false } = props
14
15
const { i18n } = useTranslation()
16
17
if (label) {
18
return (<span>
19
{getTranslation(label, i18n)}
20
{required && <span className="required">*</span>}
21
</span>);
22
}
23
24
return null
25
}
26
03

Serverless support

Payload's deep integration with the Next.js and native support for serverless architectures, like Vercel, makes it the de facto CMS for Next.js projects.

Learn about Payload on Next
Payload and Next.js icons
04

Local API

Payload features a local API, allowing developers to perform operations directly on the server, with no HTTP layer required. Directus's SDK relies on Axios to interact, which is significantly slower and less powerful.

Explore Docs
1
// The created Post document is returned
2
const post = await payload.create({
3
collection: 'posts', // required
4
data: {
5
// required
6
title: 'sure',
7
description: 'maybe',
8
},
9
locale: 'en',
10
fallbackLocale: false,
11
user: dummyUserDoc,
12
overrideAccess: true,
13
showHiddenFields: false,
14
15
// If creating verification-enabled auth doc,
16
// you can optionally disable the email that is auto-sent
17
disableVerificationEmail: true,
18
19
// If your collection supports uploads, you can upload
20
// a file directly through the Local API by providing
21
// its full, absolute file path.
22
filePath: path.resolve(__dirname, './path-to-image.jpg'),
23
24
// Alternatively, you can directly pass a File,
25
// if file is provided, filePath will be omitted
26
file: uploadedFile,
27
})
28
05

Field-level Localization

Payload features deep field-based localization support. Maintaining as many locales as you need is easy. Only need to localize a few fields? No problem, all other fields will use your fallback locale.

Explore Docs
1
import { buildConfig } from 'payload/config'
2
3
export default buildConfig({
4
collections: [
5
// collections go here
6
],
7
localization: {
8
locales: [
9
{
10
label: 'English',
11
code: 'en',
12
},
13
{
14
label: 'Arabic',
15
code: 'ar',
16
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left) when current locale is rtl
17
rtl: true,
18
},
19
],
20
defaultLocale: 'en',
21
fallback: true,
22
},
23
})
24
06

Powerful hooks

Both document and field-level hooks expose a ton of potential. Customize output, sanitize incoming data, or easily integrate with third-party platforms.

Explore Docs
1
import type { FieldHook } from 'payload/types'
2
3
// Field hook type is a generic that takes three arguments:
4
// 1: The document type
5
// 2: The value type
6
// 3: The sibling data type
7
8
type ExampleFieldHook = FieldHook<ExampleDocumentType, string, SiblingDataType>
9
10
const exampleFieldHook: ExampleFieldHook = (args) => {
11
const {
12
value, // Typed as `string` as shown above
13
data, // Typed as a Partial of your ExampleDocumentType
14
siblingData, // Typed as a Partial of SiblingDataType
15
originalDoc, // Typed as ExampleDocumentType
16
operation,
17
req,
18
} = args
19
20
// Do something here...
21
22
return value // should return a string as typed above, undefined, or null
23
}
24
07

Database flexibility

Payload gives you the choice of MongoDB or Postgres, both allow for a flexible document structure which is perfect for a CMS. Directus provides only one SQL-based database option, which requires overhead like migrations and more.

Explore Docs
1
import { postgresAdapter } from '@payloadcms/db-postgres'
2
3
export default buildConfig({
4
// Your config goes here
5
collections: [
6
// Collections go here
7
],
8
// Here is where you pass your database adapter
9
// and the adapter will require options specific to itself
10
db: postgresAdapter({
11
pool: {
12
connectionString: process.env.DATABASE_URI,
13
}
14
}),
15
})
16

Connect with us.

Whether you need help from our active community or have questions about using Payload at the enterprise level, we’re here to help.