Skip to content

blog · side by side

Payload blog vs Sanity blog for a Next.js app

Both fill the blog slot, so a generated repo carries one or the other, never both. Every line below is read out of the two manifests.

Short answer

Pick Payload blog if

Teams who want editors in an admin UI but will not put their content in someone else's database. Strong when CMS content joins application data. Also when you need custom fields, hooks or access rules a hosted CMS will not give you. Or when data residency and export matter.

Pick Sanity blog if

Teams where the people writing are not the people deploying: marketing, content and design working next to engineering. Best when content is structured (authors, categories, references between documents), not a pile of long markdown files. Also when you need previews, roles and a real-time editor without building one.

Side by side

Price, obligations, and the surface each one adds. No row is written by hand. This is manifest.yaml, rendered.

Payload blog compared with Sanity blog on pricing, fit, trade-offs, required companions, environment variables, dependencies, MCP servers and repo footprint.
From the manifestOption APayload blogOption BSanity blog
In one linePayload blog

A full CMS in your own repo and database. No second service, no content API bill.

Sanity blog

A real editorial CMS your writers can use, with the schema still living in your repo.

PricingPayload blog

Free and open source (MIT). You host it, so the cost is the Postgres and Vercel functions you already pay for. Payload also sells an Enterprise plan with dedicated support and SSO. It has no public price.

Sanity blog

Free plan: up to 20 seats, 2 public datasets and 10k documents. Growth is $15 per seat per month: up to 50 seats, private datasets and 25k documents. Past its included quotas, Growth bills pay-as-you-go. Enterprise is custom.

Best forPayload blog

Teams who want editors in an admin UI but will not put their content in someone else's database. Strong when CMS content joins application data. Also when you need custom fields, hooks or access rules a hosted CMS will not give you. Or when data residency and export matter.

Sanity blog

Teams where the people writing are not the people deploying: marketing, content and design working next to engineering. Best when content is structured (authors, categories, references between documents), not a pile of long markdown files. Also when you need previews, roles and a real-time editor without building one.

Trade-offsVerbatim from the manifestPayload blog
  • You operate it. Migrations, backups, upgrades and the admin bundle's build time are yours. A hosted CMS has none of that, and that is the whole trade.
  • It shares your database connection budget. The admin panel and your app draw from the same pool, so on serverless a pooled connection string is not optional.
  • Cold starts are real. The admin routes load Payload, the database driver and the editor bundle, so the first hit after idle is slow. It is an internal tool, so this is usually fine. Tell your editors before they report it as a bug.
  • Uploads need an object store before you deploy. Local disk works on your laptop and disappears on serverless. The media collection ships pointed at public/media and must be repointed.
  • Schema changes are code plus a migration. That is a feature in review and a speed bump when an editor asks for one more field.
Sanity blog
  • Content lives in someone else's database. Losing access to the project, or a pricing change, is a business risk a folder of markdown does not have. Export regularly. The dataset export is one CLI command.
  • GROQ is a new query language for the team. Projections make over-fetching easy to avoid, but nobody arrives knowing it, and a bad projection fails silently.
  • Caching becomes your job. Content changes without a deploy, so you need an answer to "why is this page stale?". The tags and webhook route in this battery are that answer.
  • The Studio is a dependency of your app. Mounting it at /studio adds Sanity and styled-components to your install. The route is static and code-split, but the install is bigger.
  • Sanity Studio v6 needs Node.js 22.12 or later, and next-sanity 13 needs Next.js 16. The generated package.json sets engines.node to >=22.12, which Vercel reads. On Node 20 the app still builds, but the sanity CLI refuses to run. Set Node 22 or 24 on any other host or build image.
  • Structured content costs more upfront and pays off later. Modelling authors and categories as references takes an afternoon and saves you the day you want an author page.
Required companionsAdded for you, with a reasonPayload blog
  • A database battery
Sanity blog

Nothing. It stands on its own.

Recommended alongsideSuggested, never added for youPayload blog
  • A file storage battery
Sanity blog

Nothing suggested.

Env vars you will manageEvery one documented in docs/onboard.mdPayload blog

1 variable · 1 required

  • PAYLOAD_SECRET
Sanity blog

4 variables · 4 required

  • NEXT_PUBLIC_SANITY_DATASET
  • NEXT_PUBLIC_SANITY_PROJECT_ID
  • SANITY_API_READ_TOKEN
  • SANITY_REVALIDATE_SECRET
Dependencies addedPayload blog
  • @payloadcms/db-postgres ^3.90.2
  • @payloadcms/next ^3.90.2
  • @payloadcms/richtext-lexical ^3.90.2
  • payload ^3.90.2
  • sharp ^0.35.4
Sanity blog
  • @portabletext/react ^8
  • @sanity/image-url ^2.1.1
  • @sanity/vision ^6.16.0
  • @types/node ^22 (dev)
  • next-sanity ^13.3.4
  • sanity ^6.16.0
  • styled-components ^6.1.15
MCP serversWritten into .mcp.jsonPayload blog

None. No extra agent tools from this one.

Sanity blog

None. No extra agent tools from this one.

Footprint in your repoPayload blog

17 files, plus 5 injections into shared stack files

Sanity blog

21 files, plus 5 injections into shared stack files

What changes in your repo

The paths each battery contributes, diffed. A path in the third list is written by both, so swapping rewrites that file rather than adding one.

Only with Payload blog (17)

  • src/16 files
    • app/9 files
      • (frontend)/3 files
        • api/1 file
          • blog-preview/1 file
            • route.ts
        • blog/2 files
          • [slug]/1 file
            • page.tsx
          • page.tsx
      • (payload)/6 files
        • cms/3 files
          • [[...segments]]/2 files
            • not-found.tsx
            • page.tsx
          • importMap.ts
        • cms-api/2 files
          • [...slug]/1 file
            • route.ts
          • graphql/1 file
            • route.ts
        • layout.tsx
    • components/1 file
      • cms/1 file
        • rich-text.tsx
    • payload/6 files
      • collections/3 files
        • media.ts
        • posts.ts
        • users.ts
      • migrations/1 file
        • index.ts
      • access.ts
      • local.ts
  • payload.config.ts

Only with Sanity blog (21)

  • sanity/10 files
    • lib/5 files
      • client.ts
      • fetch.ts
      • image.ts
      • queries.ts
      • types.ts
    • schemas/4 files
      • author.ts
      • block-content.ts
      • index.ts
      • post.ts
    • env.ts
  • src/9 files
    • app/6 files
      • (sanity)/2 files
        • blog/2 files
          • [slug]/1 file
            • page.tsx
          • page.tsx
      • api/3 files
        • draft-mode/2 files
          • disable/1 file
            • route.ts
          • enable/1 file
            • route.ts
        • sanity/1 file
          • revalidate/1 file
            • route.ts
      • studio/1 file
        • [[...tool]]/1 file
          • page.tsx
    • components/3 files
      • sanity/3 files
        • draft-banner.tsx
        • portable-text.tsx
        • sanity-image.tsx
  • sanity.cli.ts
  • sanity.config.ts

Shared stack files Payload blog injects into

  • bare-route-groups
  • env-required
  • nav-links
  • next-config-wrappers
  • verify-checks

Shared stack files Sanity blog injects into

  • bare-route-groups
  • env-required
  • legal-processors
  • nav-links
  • verify-checks

Payload blog in your .env.local

.env.localbash2 lines
# required
PAYLOAD_SECRET=replace-with-32-random-characters

Sanity blog in your .env.local

.env.localbash5 lines
# required
NEXT_PUBLIC_SANITY_DATASET=production
NEXT_PUBLIC_SANITY_PROJECT_ID=abc12xyz
SANITY_API_READ_TOKEN=sk_replace_me
SANITY_REVALIDATE_SECRET=replace-with-32-random-characters

What changes for your agents

Each battery ships rules, skills, subagents and hooks that an agent loads before it touches the code that battery owns. Picking one is also picking how your agents behave in payload.config.ts.

Payload blog

  • 2

    Skills

  • 2

    Rules

  • 5

    Solution docs

Rules (2)

Read Payload through the local API, never over HTTP

src/app/(frontend)/** · src/app/(payload)/** · src/payload/local.ts · src/components/cms/**

Collections are code, and every change ships with a migration

payload.config.ts · src/payload/**

Skills (2)

/add-collection

Add a Payload collection end to end: fields, explicit access control, migration, regenerated types and the page that renders it.

/payload-migrate

Create, review, apply and deploy Payload migrations safely, including renames, backfills and what to do when a migration fails halfway.

Subagents and hooks

None of its own. The foundation agents and guard hooks still ship.

Sanity blog

  • 2

    Skills

  • 2

    Rules

  • 5

    Solution docs

Rules (2)

Read tokens stay on the server, Portable Text uses design tokens

src/app/(sanity)/** · src/app/studio/** · src/app/api/draft-mode/** · src/app/api/sanity/** · src/components/sanity/** · sanity/lib/**

The schema is code, and every GROQ query lives in one file

sanity/** · sanity.config.ts

Skills (2)

/add-sanity-type

Add or extend a Sanity document type end to end (schema, GROQ projection, result type, renderer and cache tags) so nothing renders blank.

/preview-draft

Set up or debug Sanity draft previews: the enable route, the secret, the banner, and why an editor is still seeing published content.

Subagents and hooks

None of its own. The foundation agents and guard hooks still ship.

What each one already knows

Solution docs land in docs/solutions/ in your repo and are published here, so you can read the failure modes before you commit.

Payload blog (5)

Sanity blog (5)

Which one to pick

From meta.bestFor and meta.tradeoffs. If a claim is not in the manifest, it is not on this page.

Pick Payload blog when

Teams who want editors in an admin UI but will not put their content in someone else's database. Strong when CMS content joins application data. Also when you need custom fields, hooks or access rules a hosted CMS will not give you. Or when data residency and export matter.

And accept that(5)
  • You operate it. Migrations, backups, upgrades and the admin bundle's build time are yours. A hosted CMS has none of that, and that is the whole trade.
  • It shares your database connection budget. The admin panel and your app draw from the same pool, so on serverless a pooled connection string is not optional.
  • Cold starts are real. The admin routes load Payload, the database driver and the editor bundle, so the first hit after idle is slow. It is an internal tool, so this is usually fine. Tell your editors before they report it as a bug.
  • Uploads need an object store before you deploy. Local disk works on your laptop and disappears on serverless. The media collection ships pointed at public/media and must be repointed.
  • Schema changes are code plus a migration. That is a feature in review and a speed bump when an editor asks for one more field.

Pick Sanity blog when

Teams where the people writing are not the people deploying: marketing, content and design working next to engineering. Best when content is structured (authors, categories, references between documents), not a pile of long markdown files. Also when you need previews, roles and a real-time editor without building one.

And accept that(6)
  • Content lives in someone else's database. Losing access to the project, or a pricing change, is a business risk a folder of markdown does not have. Export regularly. The dataset export is one CLI command.
  • GROQ is a new query language for the team. Projections make over-fetching easy to avoid, but nobody arrives knowing it, and a bad projection fails silently.
  • Caching becomes your job. Content changes without a deploy, so you need an answer to "why is this page stale?". The tags and webhook route in this battery are that answer.
  • The Studio is a dependency of your app. Mounting it at /studio adds Sanity and styled-components to your install. The route is static and code-split, but the install is bigger.
  • Sanity Studio v6 needs Node.js 22.12 or later, and next-sanity 13 needs Next.js 16. The generated package.json sets engines.node to >=22.12, which Vercel reads. On Node 20 the app still builds, but the sanity CLI refuses to run. Set Node 22 or 24 on any other host or build image.
  • Structured content costs more upfront and pays off later. Modelling authors and categories as references takes an afternoon and saves you the day you want an author page.

Questions people actually ask

Should I choose Payload blog or Sanity blog?
Payload blog is best for teams who want editors in an admin UI but will not put their content in someone else's database. Strong when CMS content joins application data. Also when you need custom fields, hooks or access rules a hosted CMS will not give you. Or when data residency and export matter. Sanity blog is best for teams where the people writing are not the people deploying: marketing, content and design working next to engineering. Best when content is structured (authors, categories, references between documents), not a pile of long markdown files. Also when you need previews, roles and a real-time editor without building one. Both fill the blog slot, so a generated repo carries one or the other, never both.
How much do Payload blog and Sanity blog cost?
Payload blog: Free and open source (MIT). You host it, so the cost is the Postgres and Vercel functions you already pay for. Payload also sells an Enterprise plan with dedicated support and SSO. It has no public price. Sanity blog: Free plan: up to 20 seats, 2 public datasets and 10k documents. Growth is $15 per seat per month: up to 50 seats, private datasets and 25k documents. Past its included quotas, Growth bills pay-as-you-go. Enterprise is custom.
What changes in my repo if I switch from Payload blog to Sanity blog?
Payload blog writes 17 files, 1 environment variable and 5 dependencies, and installs 2 path-scoped rules, 2 skills and 5 solution docs. Sanity blog writes 21 files, 4 environment variables and 7 dependencies, and installs 2 path-scoped rules, 2 skills and 5 solution docs.

Decide once, then build the repo that already knows the decision.

Either way you get that choice’s rules, skills and solution docs installed, plus the guard hooks, an onboarding doc for exactly these env vars, and the Compound Engineering loop. Free and MIT.