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

Payload vs Directus: Speed test

In a recent performance test, we wanted to see how a real-world, complex document query might fare while retrieved from the three different CMS' GraphQL endpoints.

01

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, is significantly less popular alternative.

Read the 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
02

Local Node API

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

Read the 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
03

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.

Read the 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
04

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. A truly powerful pattern.

Read the 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
05

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.

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.