DEV Community

Alex Spinov
Alex Spinov

Posted on

Payload CMS Has a Free Headless CMS That Lives Inside Your Next.js App

Contentful charges per user. Strapi needs a separate server. Sanity has usage limits. Payload CMS lives INSIDE your Next.js app — same codebase, same deployment, same database. And it's 100% free and open source.

What Payload Gives You for Free

  • Embedded in Next.js — not a separate service, it's in your app
  • Admin panel — auto-generated from your config
  • TypeScript-first — generated types from your collections
  • Auth built-in — users, roles, permissions, API keys
  • File uploads — local, S3, Cloudflare R2
  • Rich text — Lexical editor (or Slate)
  • Drafts & versions — content versioning built in
  • Access control — field-level permissions
  • PostgreSQL or MongoDB — you choose

Quick Start

npx create-payload-app@latest
Enter fullscreen mode Exit fullscreen mode

Define Collections (Code-First Schema)

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

export const Posts: CollectionConfig = {
  slug: 'posts',
  admin: { useAsTitle: 'title' },
  access: {
    read: () => true,
    create: ({ req: { user } }) => Boolean(user),
    update: ({ req: { user } }) => Boolean(user),
    delete: ({ req: { user } }) => user?.role === 'admin',
  },
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'slug', type: 'text', unique: true },
    {
      name: 'content',
      type: 'richText',
    },
    {
      name: 'author',
      type: 'relationship',
      relationTo: 'users',
    },
    {
      name: 'status',
      type: 'select',
      options: ['draft', 'published'],
      defaultValue: 'draft',
    },
    {
      name: 'featuredImage',
      type: 'upload',
      relationTo: 'media',
    },
    {
      name: 'tags',
      type: 'array',
      fields: [{ name: 'tag', type: 'text' }],
    },
  ],
};
Enter fullscreen mode Exit fullscreen mode

Use In Your Next.js Pages

// app/blog/page.tsx
import { getPayload } from 'payload';
import config from '@payload-config';

export default async function BlogPage() {
  const payload = await getPayload({ config });

  const { docs: posts } = await payload.find({
    collection: 'posts',
    where: { status: { equals: 'published' } },
    sort: '-createdAt',
    limit: 10,
  });

  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
        </article>
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

No API calls. No fetch. Direct database access in your server components.

Payload vs Contentful vs Strapi vs Sanity

Feature Payload Contentful Strapi Sanity
Price Free (self-hosted) $300+/mo Free (self-hosted) Usage-based
Deployment Inside your app Separate service Separate server Hosted
TypeScript First-class SDK only Plugin SDK only
Database PostgreSQL/MongoDB Hosted SQLite/PostgreSQL Hosted
Auth Built-in Separate Built-in Separate
Vendor lock-in Zero High Low Medium

The Verdict

Payload is the CMS that lives inside your app. No separate service, no external API calls, no vendor lock-in. Code-first config, TypeScript types, and a beautiful admin panel — all in your Next.js codebase.


Need help building production web scrapers or data pipelines? I build custom solutions. Reach out: spinov001@gmail.com

Check out my awesome-web-scraping collection — 400+ tools for extracting web data.

Top comments (0)