Skip to content

blog · side by side

MDX 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 MDX blog if

Engineering blogs, changelogs, docs-adjacent writing and any site where the writers already have commit access. Best when posts should go through review with the code they describe. Also when a post needs to import a real component from the app.

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.

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

Posts are files in your repo. Reviewed in a pull request, shipped with the code.

Sanity blog

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

PricingMDX blog

Free. No vendor, no seats, no API quota. The only cost is build time.

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 forMDX blog

Engineering blogs, changelogs, docs-adjacent writing and any site where the writers already have commit access. Best when posts should go through review with the code they describe. Also when a post needs to import a real component from the app.

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 manifestMDX blog
  • Publishing needs a deploy. A typo fix is a commit, a build and a rollout. Fine for engineers. Painful for a marketing team that expects a "publish" button.
  • No editor. Writers work in a code editor with YAML frontmatter and Git. The only preview link is a deploy preview, and the only permissions are repository access.
  • No image pipeline. Images live in public/, and you size and compress them. A hosted CMS gives you transforms and a CDN.
  • Content and code share a release cycle. Reverting a bad deploy reverts the posts published in it, and a content-only change still runs your whole build.
  • Everything is yours. No API keys to rotate, no dataset to lose access to, no pricing change to absorb. The whole archive greps in one command.
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 reasonMDX blog

Nothing. It stands on its own.

Sanity blog

Nothing. It stands on its own.

Recommended alongsideSuggested, never added for youMDX blog

Nothing suggested.

Sanity blog

Nothing suggested.

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

0 variables

None.

Sanity blog

4 variables · 4 required

  • NEXT_PUBLIC_SANITY_DATASET
  • NEXT_PUBLIC_SANITY_PROJECT_ID
  • SANITY_API_READ_TOKEN
  • SANITY_REVALIDATE_SECRET
Dependencies addedMDX blog
  • @mdx-js/loader ^3 (dev)
  • @mdx-js/react ^3
  • @next/mdx ^16
  • @types/mdx ^2 (dev)
  • gray-matter ^4.0.3
  • rehype-slug ^6
  • remark-frontmatter ^5
  • remark-gfm ^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.jsonMDX blog

None. No extra agent tools from this one.

Sanity blog

None. No extra agent tools from this one.

Footprint in your repoMDX blog

13 files, plus 3 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 MDX blog (13)

  • content/2 files
    • blog/2 files
      • hello-world.mdx
      • writing-with-mdx.mdx
  • scripts/1 file
    • blog/1 file
      • check-posts.ts
  • src/10 files
    • app/5 files
      • blog/4 files
        • [slug]/2 files
          • opengraph-image.tsx
          • page.tsx
        • rss.xml/1 file
          • route.ts
        • page.tsx
      • sitemap.ts
    • components/2 files
      • blog/2 files
        • callout.tsx
        • post-card.tsx
    • lib/2 files
      • blog/2 files
        • frontmatter.ts
        • posts.ts
    • mdx-components.tsx

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 MDX blog injects into

  • 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

MDX blog in your .env.local

.env.localbash1 line
# MDX blog adds no environment variables.

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 content/**.

MDX blog

  • 2

    Skills

  • 2

    Rules

  • 5

    Solution docs

Rules (2)

Posts are validated content, not free-form files

content/**

The blog compiles at build time and styles itself with tokens

src/app/blog/** · src/app/sitemap.ts · src/lib/blog/** · src/components/blog/** · src/mdx-components.tsx

Skills (2)

/add-mdx-component

Add a component that posts can use without importing it, registered in src/mdx-components.tsx and styled with design tokens only.

/new-post

Draft, validate and publish a new MDX post in content/blog, with frontmatter that passes the checker and a slug that will never change.

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.

MDX 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 MDX blog when

Engineering blogs, changelogs, docs-adjacent writing and any site where the writers already have commit access. Best when posts should go through review with the code they describe. Also when a post needs to import a real component from the app.

And accept that(5)
  • Publishing needs a deploy. A typo fix is a commit, a build and a rollout. Fine for engineers. Painful for a marketing team that expects a "publish" button.
  • No editor. Writers work in a code editor with YAML frontmatter and Git. The only preview link is a deploy preview, and the only permissions are repository access.
  • No image pipeline. Images live in public/, and you size and compress them. A hosted CMS gives you transforms and a CDN.
  • Content and code share a release cycle. Reverting a bad deploy reverts the posts published in it, and a content-only change still runs your whole build.
  • Everything is yours. No API keys to rotate, no dataset to lose access to, no pricing change to absorb. The whole archive greps in one command.

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 MDX blog or Sanity blog?
MDX blog is best for engineering blogs, changelogs, docs-adjacent writing and any site where the writers already have commit access. Best when posts should go through review with the code they describe. Also when a post needs to import a real component from the app. 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 MDX blog and Sanity blog cost?
MDX blog: Free. No vendor, no seats, no API quota. The only cost is build time. 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 MDX blog to Sanity blog?
MDX blog writes 13 files, 0 environment variables and 8 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.