Payload CMS and Directus are both open-source, self-hosted headless CMS platforms. They take opposite approaches to schema. Payload is code-first: you define collections in TypeScript, and the database follows your code. Directus is database-first: it reads an existing database and builds an admin UI around it. Pick based on whether your schema should live in Git or in the database.

Key takeaways

  • Payload CMS is code-first: schema lives in TypeScript config files and syncs to Git. This suits new Next.js projects.
  • Directus is database-first: it reads an existing database without changing your tables. This suits legacy systems and non-technical schema editors.
  • Payload's Local API skips HTTP when the CMS runs inside the same Next.js app. Directus runs as a separate service reachable from any frontend over REST or GraphQL.
  • Both are free to self-host under their open-source licenses. Both offer managed cloud hosting at similar starting prices.
  • Pick Payload for TypeScript-heavy teams building new apps tied to Next.js. Pick Directus for multi-frontend projects or when you must manage an existing database.

Updated 15 August 2026: sources added, experience claims checked against our project record, summary added.

Payload CMS vs Directus in 2026: Code-First vs Database-First

The Core Philosophy Split

Picture two projects on your screen side by side: in one, your schema sits in a TypeScript file next to your app code; in the other, it lives inside the database you already have running.

Payload CMS is code-first. You define your schema in TypeScript config files. The database follows your code.

Directus is database-first. You can point it at an existing database and it reads the schema. The admin UI follows your database.

Neither approach is better on its own. But one will be much better for your project. Getting this wrong causes pain later.

If you're building a new project and want your CMS schema tracked in Git next to your app code, Payload will feel like home. If you have an existing PostgreSQL database with 200 tables and need a content layer on top of it, Directus will save you weeks.

Schema Design: Code-First vs Database-First

Payload's Code-First Approach

Open a fresh Payload project and you'll see your collections defined right there in TypeScript config files, in Payload 3.x (the current major version as of 2026):

// collections/Posts.ts
import type { CollectionConfig } from 'payload'

export const Posts: CollectionConfig = {
  slug: 'posts',
  admin: {
    useAsTitle: 'title',
  },
  fields: [
    {
      name: 'title',
      type: 'text',
      required: true,
    },
    {
      name: 'content',
      type: 'richText',
    },
    {
      name: 'author',
      type: 'relationship',
      relationTo: 'users',
    },
    {
      name: 'publishedAt',
      type: 'date',
    },
    {
      name: 'status',
      type: 'select',
      options: [
        { label: 'Draft', value: 'draft' },
        { label: 'Published', value: 'published' },
      ],
      defaultValue: 'draft',
    },
  ],
}

This config is your source of truth. When Payload starts, it builds database migrations on its own (Payload 3.x uses Drizzle ORM under the hood with PostgreSQL or SQLite support, and still supports MongoDB). Your schema lives in Git, so you review schema changes in PRs. It's the same workflow you'd use for app code. That's the point.

Payload also builds TypeScript types from these configs on its own. When you query posts in your Next.js frontend, you get full type safety without keeping separate type files.

Directus's Database-First Approach

Point Directus at a database you already have, and watch it read the schema back to you without asking you to redefine anything. You can:

  1. Point Directus at an existing database and it reads the schema
  2. Create collections through the admin UI, which builds SQL migrations
  3. Use the Directus SDK to manage schema in code
// Creating a collection via Directus SDK
import { createDirectus, rest, createCollection } from '@directus/sdk'

const client = createDirectus('http://localhost:8055').with(rest())

await client.request(
  createCollection({
    collection: 'posts',
    schema: {
      name: 'posts',
    },
    meta: {
      icon: 'article',
      note: 'Blog posts',
    },
  })
)

Directus 11, released in late 2025, made big improvements to schema migration tools. You can now export and import schema snapshots as YAML files. This makes version control more practical than it used to be:

## Export current schema
npx directus schema snapshot ./schema-snapshot.yaml

## Apply schema diff to another environment
npx directus schema apply ./schema-snapshot.yaml

Even with these gains, Directus schema management doesn't feel as natural in a Git workflow as Payload's approach. You're snapshotting state rather than declaring intent.

Schema Comparison Table

Aspect Payload CMS Directus
Schema source of truth TypeScript config files Database itself
Schema versioning Native Git workflow YAML snapshots (improved in v11)
Existing database support Limited (migration path) Excellent (introspection)
Auto-generated types Yes, from config Yes, via SDK + CLI
Database support PostgreSQL, SQLite, MongoDB PostgreSQL, MySQL, MariaDB, SQLite, MS SQL, CockroachDB, OracleDB
ORM / Query layer Drizzle ORM (v3) Custom query engine (Knex-based)
Migration generation Automatic from config changes Schema diff snapshots

TypeScript and Developer Experience

Payload's TypeScript Story

Run payload generate:types and you'll watch a full set of interfaces appear for every collection you've defined, since Payload is written entirely in TypeScript and configured in TypeScript:

// Auto-generated
export interface Post {
  id: string
  title: string
  content?: RichTextContent
  author?: string | User
  publishedAt?: string
  status?: 'draft' | 'published'
  createdAt: string
  updatedAt: string
}

These types flow through your Local API, REST API responses, and GraphQL queries. In Payload 3.x, the CMS runs inside your Next.js app, so you can import and use these types directly. You don't need a separate SDK, and there are no API calls for server-side rendering. You query the database directly:

// In a Next.js Server Component
import { getPayload } from 'payload'
import config from '@payload-config'

export default async function BlogPage() {
  const payload = await getPayload({ config })
  
  const posts = await payload.find({
    collection: 'posts',
    where: {
      status: { equals: 'published' },
    },
  })
  // posts.docs is fully typed as Post[]
  
  return <PostList posts={posts.docs} />
}

This is a strong developer experience. There's no network hop, full types, and it's just functions.

Directus's TypeScript Story

You'll notice the improvement right away if you've used the Directus SDK before, since the @directus/sdk package now supports generic type parameters:

import { createDirectus, rest, readItems } from '@directus/sdk'

interface Schema {
  posts: Post[]
  users: User[]
}

interface Post {
  id: number
  title: string
  content: string
  author: number | User
  published_at: string
  status: 'draft' | 'published'
}

const client = createDirectus<Schema>('http://localhost:8055').with(rest())

const posts = await client.request(
  readItems('posts', {
    filter: { status: { _eq: 'published' } },
    fields: ['id', 'title', 'content', 'author.*'],
  })
)

The catch: you have to write and keep up those type definitions yourself, or generate them with community tools like directus-typescript-gen. The types don't come from the schema on their own the way Payload's do. This is the trade-off of database-first design. The database itself doesn't know about TypeScript.

Payload CMS vs Directus in 2026: Code-First vs Database-First - architecture

API Layer and Query Capabilities

You'll feel the difference in flavor the moment you start writing queries against either platform, since both build REST and GraphQL APIs.

Payload gives you:

  • REST API with depth control for relationships
  • GraphQL API (built on its own from your config)
  • Local API (direct database queries, no HTTP overhead)
  • Full query operators: equals, not_equals, greater_than, in, contains, etc.

Directus gives you:

  • REST API with granular field selection
  • GraphQL API (built on its own from schema introspection)
  • Directus SDK (wraps REST, typed if you provide interfaces)
  • Rich filtering with _eq, _neq, _gt, _in, _contains, logical _and/_or operators
  • Aggregation queries built into the API (count, sum, avg, etc.)

Directus has a slight edge on API flexibility for complex queries, especially aggregations. If you need GROUP BY style queries through the API, Directus handles that natively. With Payload, you'd usually drop down to the Drizzle ORM layer or write a custom endpoint.

Payload's key advantage is the Local API. When your CMS and your Next.js frontend are the same process, you skip HTTP entirely. For server-rendered pages, this means faster builds and lower latency.

Admin Panel and Content Editing

Open Payload 3.x's admin panel and you're looking at a React app that ships as part of your Next.js app, ready for you to reshape almost any piece of the UI. You can customize it with React components and override almost any piece of the UI. The block-based rich text editor, built on Lexical as of v3, is powerful and easy to extend.

Directus's admin panel is a standalone Vue.js app (Directus Data Studio). It's polished, looks strong, and non-technical users tend to pick it up fast. The flow and automation builder in Directus is more visual and easier to use than Payload's hooks system.

Feature Payload CMS Directus
Admin framework React (Next.js) Vue.js (standalone)
Rich text editor Lexical-based TipTap / WYSIWYG
Custom fields React components Vue extensions
Workflow/Automation Hooks + custom endpoints Directus Flows (visual builder)
Localization Built-in field-level i18n Built-in field-level i18n
Content versioning Draft/publish + versions Content versioning + revisions
File management Built-in media library Built-in media library with transforms

In practice, for developer-heavy teams, Payload's admin is easier to extend because you're writing React. For teams where content editors are the main users and low-friction UX matters most, Directus's admin panel is a bit more approachable out of the box.

Authentication and Access Control

You'll set up login, roles, and permissions on both systems, but you'll notice they get there in different ways.

Payload uses collection-based auth. You mark a collection as an auth collection (usually users), and it gets login, registration, password reset, email verification, and JWT or cookie-based sessions. Access control is set per-collection and per-field using functions:

{
  slug: 'posts',
  access: {
    read: () => true, // Public
    create: ({ req: { user } }) => Boolean(user), // Authenticated
    update: ({ req: { user } }) => user?.role === 'admin',
    delete: ({ req: { user } }) => user?.role === 'admin',
  },
  fields: [
    {
      name: 'internalNotes',
      type: 'text',
      access: {
        read: ({ req: { user } }) => user?.role === 'admin',
      },
    },
  ],
}

This is powerful. Access control functions get the full request context, so you can build almost any pattern, including RBAC, ABAC, multi-tenancy, or row-level security.

Directus uses a role-based permission system set up through the admin panel. You create roles, assign granular permissions (CRUDS per collection), and add custom permissions with filters. It's visual and easy to use, but less flexible for complex patterns that don't fit the role model.

For most projects, both work fine. For multi-tenant SaaS or complex authorization logic, Payload's code-based access control is hard to beat.

Performance and Scalability

You'll notice the latency difference depending on how your query actually reaches the database, in production PostgreSQL backends.

Metric Payload CMS 3.x Directus 11
Simple read (single item) Fast via Local API; REST adds typical HTTP overhead REST-only; typical HTTP overhead per request
List query, no relations Local API avoids the network hop Handled efficiently over REST
List query, deep relations Slower as relation depth grows, even via Local API Slower as relation depth grows over REST
Cold start Tied to Next.js startup time Starts as a standalone Node process
Memory baseline Higher, since Next.js runs alongside the CMS Lower, since Directus runs on its own

Payload's Local API advantage is real for SSR and SSG workloads. When a build creates many static pages, skipping HTTP for every query cuts build time.

Directus handles high-throughput REST workloads well, and its Redis-backed caching layer is mature.

Deployment and Hosting

You can drop Payload 3.x onto nearly any Next.js hosting target, since it's a Next.js app at its core: Vercel, Netlify, AWS, Docker, and more. Payload Cloud, its managed hosting option, starts at $30 per month for production projects as of early 2026.

Directus runs as a standalone Node.js app. Docker is the recommended way to deploy it, and Directus Cloud starts at $29 per month. Self-hosting on a VPS is simple, since it's just a Docker container that needs a database connection.

One thing worth noting: since Payload 3.x is your Next.js app, your CMS and frontend deploy together. This makes infrastructure simpler, but your CMS admin panel scales with your frontend. For high-traffic sites where the frontend and CMS need different scaling, running them apart may be worth thinking about.

Directus, being a separate service, naturally keeps these concerns apart. Your frontend, whether it's Next.js, Astro, or anything else, connects to Directus over HTTP. This is a more traditional headless setup.

Social Animal built SleepDr's HIPAA-safe Next.js and Payload CMS platform, which shows the Payload-in-Next.js pattern working well for a content-heavy site with 20 city landing pages in four languages. For enterprise setups with multiple frontend consumers, a separated Directus-style setup can be cleaner, since the CMS and frontends don't share one deployment unit.

Pricing and Licensing in 2026

Payload CMS Directus
License MIT GPL-3.0 (with BSL for Cloud features)
Self-hosted cost Free Free
Cloud hosting From $30/mo (Payload Cloud) From $29/mo (Directus Cloud)
Enterprise tier Custom pricing Custom pricing
Premium features Some features Cloud-only Directus+ subscription for marketplace extensions

You can self-host either one for free, since both are truly open-source. Payload's MIT license is more permissive, so you can embed it in commercial products with no limits. Directus's GPL-3.0 license means derivative works must also be GPL, which can matter for SaaS products.

Directus launched Directus+ in late 2025, a subscription that unlocks premium marketplace extensions and priority support. It's optional, but some advanced extensions, like the AI content assistant, sit behind this paywall.

When to Choose Which

Choose Payload CMS when:

  • You're building a new project from scratch (greenfield)
  • Your team is TypeScript-heavy and wants schema-as-code
  • You're using Next.js and want the tightest integration
  • You need complex, code-defined access control
  • You want everything in one deployable unit
  • You value MIT licensing

Choose Directus when:

  • You have an existing database you need to manage
  • Your team includes non-developers who need to change schemas
  • You need to support multiple frontends (web, mobile, IoT) from one CMS
  • You want a visual automation/flow builder
  • You need broad database support (MySQL, MSSQL, Oracle, etc.)
  • You prefer the CMS as a separate, standalone service

If you're weighing these options for a headless project and want a second opinion, we do headless CMS architecture consulting and can help you pick the right tool before you're three months into the wrong one.

For projects using Astro as the frontend framework, Directus's standalone API approach fits well, since Astro fetches data at build time or via server endpoints. Payload works too, but you lose the Local API edge since Astro and Payload would run as separate processes.

FAQ

Is Payload CMS really free in 2026?

Yes. Payload CMS is MIT-licensed and fully free to self-host. Payload Cloud is their paid managed hosting service, but the CMS itself, including all core features, is open source. There are no feature gates on the self-hosted version.

Can Directus work with an existing database without changing it?

Mostly yes. Directus can read an existing database and build a management layer on top of it. It adds a few system tables (prefixed with directus_) for its own setup, but it doesn't change your existing tables. This is one of its strongest points.

Which is better for a solo developer?

Payload CMS tends to be the favorite among solo developers who know TypeScript well. The code-first workflow lets you set up collections fast, and the auto-generated types cut boilerplate. Directus is better if you want a visual interface for quick schema prototyping without writing config files.

Can I use Payload CMS without Next.js?

As of Payload 3.x, Next.js is the main adapter, and the admin panel runs on Next.js. The REST and GraphQL APIs, though, work with any frontend. You're not locked into Next.js for your consumer-facing site. You just need Next.js to run the Payload admin. There have been community talks about other adapters, like Nuxt, but nothing official yet.

How does Directus handle content localization?

Directus supports field-level translations. You mark fields as translatable, set up your languages, and Directus stores translations in a related table. The API lets you request content in specific languages via the ?fields and language parameters. It's well built and has been stable for years.

Which has better plugin/extension support?

Directus has a larger extension marketplace, especially with the Directus+ additions. You can build custom interfaces, displays, endpoints, hooks, and modules. Payload's extension model is based on React component overrides and plugins, so it's powerful but the ecosystem is smaller. Payload's plugins tend to be more focused, such as SEO, form builder, and redirects, while Directus extensions cover a wider range.

Is there a performance difference for large datasets?

For datasets with millions of rows, both do well when properly indexed. Directus has a slight edge on raw query flexibility since it builds SQL more directly from API queries. Payload's Drizzle ORM layer is efficient but adds a small abstraction cost. In practice, your database tuning matters far more than which CMS you pick. Both support connection pooling and can work with read replicas.

Can I migrate from one to the other later?

Yes, but it's not simple. Content stored in a relational database can be exported and re-imported with standard database tools. Rebuilding schema definitions, access control rules, and custom logic tied to each platform's API is the harder part. Decoupling your frontend from CMS-specific responses, as with a headless architecture, makes a future migration easier for either tool.

Key takeaway:

Payload keeps schema in code. Directus keeps schema in the database.