Skip to main content

Payload CMS Starter Review 2026: Content-First SaaS

·StarterPick Team
Share:

TL;DR

Payload CMS is the right foundation for content-heavy SaaS products. It's a headless CMS and application framework in one — perfect for products where managing structured content (courses, documentation, articles, products) is a core feature. Not a SaaS boilerplate in the traditional sense; better described as an application platform. At free (MIT), the value is significant.

What Payload CMS Is

Payload 2.0+ is different from traditional CMS tools:

  • App framework: Build custom applications with Payload's API
  • Headless CMS: Structured content with auto-generated admin
  • Authentication: Built-in auth with access control
  • API-first: REST and GraphQL auto-generated
// payload.config.ts — define your data model
import { buildConfig } from 'payload/config';

export default buildConfig({
  serverURL: process.env.SERVER_URL!,
  admin: {
    user: Users.slug,
    meta: { titleSuffix: '— YourSaaS CMS' },
  },
  collections: [
    Users,           // Auth users
    Courses,         // Course content
    Lessons,         // Individual lessons
    Media,           // File uploads
    Orders,          // Purchase records
  ],
  globals: [
    SiteSettings,    // Global config
  ],
});

Collections: The Core Concept

// collections/Courses.ts
import type { CollectionConfig } from 'payload/types';

export const Courses: CollectionConfig = {
  slug: 'courses',
  admin: {
    useAsTitle: 'title',
  },
  access: {
    read: () => true,      // Public read
    create: isAdmin,       // Admin only create
    update: isAdmin,
    delete: isAdmin,
  },
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'slug', type: 'text', required: true, unique: true },
    { name: 'description', type: 'richText' },
    {
      name: 'price',
      type: 'number',
      required: true,
      min: 0,
    },
    {
      name: 'lessons',
      type: 'relationship',
      relationTo: 'lessons',
      hasMany: true,
    },
    {
      name: 'thumbnail',
      type: 'upload',
      relationTo: 'media',
    },
    {
      name: 'status',
      type: 'select',
      options: ['draft', 'published'],
      defaultValue: 'draft',
    },
  ],
  hooks: {
    afterChange: [
      async ({ doc }) => {
        // Invalidate Next.js cache on content change
        await revalidatePath(`/courses/${doc.slug}`);
      },
    ],
  },
};

This auto-generates:

  • Admin UI for managing courses
  • REST API at /api/courses
  • GraphQL queries for courses

Access Control

// Payload's access control is powerful
const isAdmin = ({ req: { user } }) => user?.role === 'admin';

const isPurchased = async ({ req, id }) => {
  if (!req.user) return false;

  const order = await payload.find({
    collection: 'orders',
    where: {
      and: [
        { purchaser: { equals: req.user.id } },
        { course: { equals: id } },
        { status: { equals: 'paid' } },
      ],
    },
  });

  return order.totalDocs > 0;
};

export const Lessons: CollectionConfig = {
  slug: 'lessons',
  access: {
    read: isPurchased,  // Only show content to buyers
  },
  // ...
};

Fine-grained access control is built into the data model.


When Payload CMS Makes Sense

Best use cases:

  • Online course platforms (lessons, modules, student access)
  • Documentation sites with gated premium content
  • SaaS where content management is a core workflow for the client
  • Products with complex structured content (product catalogs, knowledge bases)
  • White-label platforms where clients manage their own content

Less ideal for:

  • Simple subscription SaaS without significant content management
  • Real-time collaborative apps (Payload isn't built for it)
  • Mobile-first products (more complex API integration)

Payload CMS + Next.js + Stripe Pattern

The common SaaS pattern with Payload:

// app/courses/[slug]/page.tsx — Next.js + Payload
async function CoursePage({ params }) {
  const course = await payload.findOne({
    collection: 'courses',
    where: { slug: { equals: params.slug } },
  });

  const isPurchased = await checkPurchase(userId, course.id);

  if (!isPurchased) {
    return <PurchasePage course={course} />;
  }

  return <CourseContent course={course} />;
}
// After Stripe checkout — grant access
export async function POST(req: Request) {
  const event = stripe.webhooks.constructEvent(/* ... */);

  if (event.type === 'checkout.session.completed') {
    const { courseId, userId } = event.data.object.metadata;

    await payload.create({
      collection: 'orders',
      data: {
        purchaser: userId,
        course: courseId,
        status: 'paid',
        amount: event.data.object.amount_total,
      },
    });
  }
}

Limitations

  • Learning curve: Payload's config model is different from traditional SaaS patterns
  • Not a complete SaaS starter: No billing UI, no marketing pages out of the box
  • Deployment: Requires a server (no static export) — Node.js runtime
  • MongoDB required (v1) or PostgreSQL (v2+) — slightly more setup

Who Should Use Payload CMS

Good fit:

  • Content-heavy products (courses, documentation, media platforms)
  • Products where clients manage their own content
  • Teams who want a CMS and application framework in one
  • Developers building white-label content platforms

Bad fit:

  • Simple subscription SaaS
  • Teams who want full boilerplate features out of the box
  • Serverless-only environments

Final Verdict

Rating: 4/5 for its use case

Payload CMS is an excellent choice for content-driven products. It's not a traditional SaaS boilerplate, but for the right product type, it's far better than bolting a CMS onto a traditional boilerplate. The free, MIT license is exceptional value.



Getting Started with Payload CMS

# Create a new Payload project
npx create-payload-app@latest my-cms-app

# Choose template: website, ecommerce, or blank
# Choose database: MongoDB or PostgreSQL (v2+)

cd my-cms-app
cp .env.example .env
# Set: DATABASE_URI, PAYLOAD_SECRET

npm run dev
# Admin panel: http://localhost:3000/admin
# API: http://localhost:3000/api

Environment variables:

DATABASE_URI=postgresql://...     # or mongodb://...
PAYLOAD_SECRET=your-secret-key    # Used to sign JWTs
NEXT_PUBLIC_SERVER_URL=http://localhost:3000

First create an admin user at /admin/create-first-user. The admin dashboard auto-generates from your collection configs — no separate UI code needed.


Payload CMS vs Directus vs Strapi

FeaturePayload CMSDirectusStrapi
LicenseMITBusiness SourceMIT (some plugins paid)
TypeScript-firstPartialPartial
React-based adminVueReact
Next.js integration✅ nativeVia APIVia API
Access controlCode-basedGUI-basedGUI-based
Self-hosting
ComplexityMediumLowMedium

Payload's code-first approach (access control in TypeScript) is more powerful but less accessible than Directus's GUI-based permissions. Strapi is the most widely adopted, but Payload's TypeScript integration is significantly tighter for Next.js applications.

When to choose Directus: Non-technical team members need to manage both content and permissions without developer involvement. Directus's admin GUI covers permission rules, flows (workflows), and data relationships without writing code.

When to choose Payload: Your team is TypeScript-fluent and you want access control logic version-controlled alongside your application code. Payload's colocation of data model, access control, and API is genuinely powerful for complex products.


The Admin UI

Payload auto-generates a full admin panel from your collection configurations:

// The fields you define in your collection...
fields: [
  { name: 'title', type: 'text', required: true },
  { name: 'status', type: 'select', options: ['draft', 'published'] },
  { name: 'content', type: 'richText' },
]

// ...become form fields in the admin UI automatically.
// No separate admin code to write.

The admin UI supports custom components — you can replace default field editors with your own React components. This means custom image editors, map inputs, or any specialized UI can be embedded directly in the admin panel.


Deployment Considerations

Payload CMS requires a persistent server — it cannot be deployed as a fully serverless application. The admin panel and API need a Node.js runtime, which means:

  • Vercel: Works for the Next.js frontend, but Payload's server process must run separately (e.g., on Railway, Render, or Fly.io). Vercel's serverless functions have a 10-second timeout that can cause issues with Payload's initialization.
  • Railway or Render: Deploy Payload as a Node.js service. Both have free tiers that cover early-stage applications.
  • Fly.io: Recommended for Payload deployments that need persistent connections and low latency. Supports long-running Node processes and Docker-based deployment.

For the database, PostgreSQL (Neon or Supabase) works well with Payload v2+. MongoDB Atlas remains an option but PostgreSQL's query performance and hosting ecosystem are generally preferable for new projects. Enable connection pooling (PgBouncer on Neon, built-in on Supabase) to handle the connection overhead from serverless-adjacent deployments.

Payload v3: Running Inside Next.js

Payload v3 changed the deployment model significantly. Rather than running as a separate Node.js process alongside your Next.js app, Payload v3 runs inside your Next.js application as an integrated framework. The payload.config.ts lives at your Next.js project root. The admin panel is served from /admin inside your app. The REST API is exposed through Next.js API routes. The database connection is shared between Payload and your application code.

This integration means one pnpm dev starts everything. One Vercel or Railway deployment serves both the Next.js frontend and the Payload admin. There is no cross-origin complexity because the admin and the app live at the same domain. Your TypeScript compilation, database migrations, and Payload schema all build together in one pipeline.

The practical impact is significant for indie developers. Payload v2 required running next dev and payload dev simultaneously, managing two processes, and handling cross-origin requests from the admin to the API. v3 collapses this into a standard Next.js development workflow. If you evaluated Payload v2 and found the dual-process setup operationally complex, v3 is worth reconsidering.


Building a Course Platform: The Full Data Model

A course platform is the clearest demonstration of Payload's value. The data model maps naturally onto collections, the access control logic is content-gated by purchase, and the admin UI gives instructors a clean interface for managing content without developer involvement.

The enrollment pattern is the core business logic. When a student completes Stripe checkout, the webhook handler creates an enrollment record linking the student's user ID to the course ID. Every subsequent read request to the lessons collection runs the access control function, which queries the enrollments collection to verify the student has paid access. This check happens at the Payload API layer — there is no way to fetch lesson content without passing through it.

This is architecturally different from implementing access control at the Next.js route handler level. With route-level gating, a bug in your authorization code can expose content directly through the API. With Payload's collection-level access control, the check is enforced regardless of which code path triggers the read. The access control function is defined once and applied everywhere — REST endpoint, GraphQL query, direct local API call.

For products with multiple content tiers — a free preview, a paid course, a premium mentorship tier — this model scales well. Each tier maps to a collection access rule or a field-level access function. Adding a new tier means adding a new rule in TypeScript, not modifying multiple route handlers across your codebase.


Self-Hosting vs Payload Cloud

Payload is MIT-licensed and runs on any Node.js host. For most indie projects, Railway is the simplest deployment target: create a project, point it at your GitHub repository, add a PostgreSQL database add-on, set environment variables. Monthly cost is $5-15 depending on traffic. No infrastructure configuration beyond that.

Vercel works for Payload v3 Next.js integrated apps, but with real caveats. Vercel's serverless function timeout (10 seconds on Pro) causes failures for bulk admin operations like importing large content sets. Long-running operations that exceed the timeout fail silently in the Payload admin. For most standard read and write operations this is not a problem, but it is a concrete constraint for data-heavy admin workflows that content teams depend on daily.

Payload Cloud is the managed hosting service offered by the Payload team. It handles deployment, PostgreSQL database provisioning, file storage, email, and CDN. Pricing starts at $25 per month. The value is operational simplicity — no infrastructure configuration, automatic backups, one-click environment cloning for staging and preview deployments. For solo developers who want to focus on product rather than DevOps, the monthly cost is reasonable at early-stage user volumes.

The decision comes down to team capacity. If infrastructure management is a distraction from product development, start on Payload Cloud. If you have existing Railway or Render infrastructure and want to minimize costs, self-host. Either path is straightforward — Payload's Docker support and environment variable configuration are standard and thoroughly documented.


Key Takeaways

  • Payload CMS is best for content-heavy SaaS products where managing structured content (courses, products, documentation) is a core workflow — not a standard SaaS boilerplate
  • The auto-generated REST and GraphQL APIs from collection configs save significant backend development time
  • Access control written in TypeScript (co-located with your data model) is more maintainable than GUI-based rules for complex permission requirements
  • Payload v3 runs inside Next.js, eliminating the dual-process setup required in v2
  • Payload v2+ supports PostgreSQL, dropping the MongoDB-only requirement of v1
  • For simple subscription SaaS without significant content management, a traditional boilerplate (ShipFast, Supastarter) is more appropriate

Find content-platform boilerplates in the best open-source SaaS boilerplates guide.

See how Payload compares for testing setup in our testing in boilerplates guide.

Compare Payload's file handling with dedicated upload services in our file upload guide.

Check out this boilerplate

View Payload CMSon StarterPick →

The SaaS Boilerplate Matrix (Free PDF)

20+ SaaS starters compared: pricing, tech stack, auth, payments, and what you actually ship with. Updated monthly. Used by 150+ founders.

Join 150+ SaaS founders. Unsubscribe in one click.