Skip to main content

Payload CMS Starter Review 2026: Content-First SaaS

·StarterPick Team
payload-cmscmsreviewboilerplate2026

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.


Find content-platform boilerplates on StarterPick.

Check out this boilerplate

View Payload CMS on StarterPick →

Comments