WB × The Multiplier / Web & development
Comparison
Astro vs Next.js for a Content Site in 2026: Notes From Running One of Each
One is a 92-piece publication on Cloudflare Pages. The other is my own studio site with a CRM behind it. The framework choice followed the second half of each sentence.
By Weston BeckwithUpdated 9 min read

If the site is mostly pages, build it on Astro. If the site is also an application, build it on Next.js. That is the answer as of September 2026, and the rest of this piece is what the difference looked like on two real builds: The Outpost, a programmatic SEO publication WBX Creative built for Power Lodge Florida on Astro 7 and Cloudflare Pages, and wbxcreative.com, the Next.js site you are reading now.
The Outpost went to production on 2026-09-17 with a content plan of 92 pieces. This site has run on Next.js since 2025 and this month got a full SEO rebuild and the blog engine rendering this page. As of 2026-09-23 it runs Next.js 15.5.26 while 16.3.6 is current; the upgrade is planned before Next 15 maintenance ends on 2026-10-21.
Neither choice was wrong. They were different answers to a question about the site, not the framework: does anything on it have to happen at request time?
The short version
- Build model. Astro prerenders everything and ships no client JavaScript unless you add an island. Next.js renders Server Components and wants a server runtime for anything dynamic.
- Content. Astro validates frontmatter with a Zod schema at build. Next.js has no content layer.
- Images. We skipped Astro's image component and pre-sized with sharp. On Vercel, next/image transformations are metered.
- Hosting. Cloudflare Pages serves the Outpost's static requests free and unlimited. Vercel's Hobby plan forbids commercial use, so a studio site belongs on Pro.
- Dynamic needs. This site runs a CRM, a REST API, and an MCP endpoint. The Outpost runs nothing at request time.
- Gates. The same quality gate runs on both. I wrote it for Astro and ported it to Next.js.
The two builds side by side
| The Outpost (Astro) | wbxcreative.com (Next.js) | |
|---|---|---|
| Version, September 2026 | Astro ^7.2.10 (7.3.4 current) | Next.js 15.5.26 (16.3.6 current) |
| Rendering | Fully prerendered, no SSR adapter | Server Components; static pages plus Node API routes |
| Content | MDX collections, glob() loader, Zod schema | MDX via gray-matter and next-mdx-remote, no schema at build |
| Images | sharp scripts pre-size; plain <img> with dimensions | next/image, AVIF and WebP, sources capped at 1,600 px |
| Sitemap | @astrojs/sitemap, production only, truthful lastmod | app/sitemap.ts, lastModified only where a date exists |
| Quality gate | 10 checks over dist/, no bypass | Same gate, reads .next/server/app |
| Dynamic needs | None at request time; the lead relay is written as a separate Worker | CRM at /admin, REST API, MCP endpoint, Resend |
How does each one build a page?
The Outpost has no SSR adapter in astro.config.mjs, so every route is HTML on disk. Astro 7, released 2026-06-22, compiles .astro files with a Rust compiler and renders Markdown and MDX through its own Rust pipeline by default instead of the remark and rehype chain, and Astro reports builds 15 to 61 percent faster in its own benchmarks. The Outpost passes no remark or rehype plugins, so the change was a non-event; if yours does, the unified pipeline is still an opt-in.
This site is React Server Components. Post pages are static at build through generateStaticParams, but the same app serves the CRM API, the MCP endpoint, and the form routes, so it needs a Node runtime. That single fact is most of the decision. The blog could be Astro. The site around it could not.
Next.js 16 changed the build itself. Turbopack is the default, and a custom webpack function in next.config.ts makes next build fail unless you pass --webpack. This site has one, a short rule for PDF, AI, and EPS files, so porting it to turbopack.rules is on the upgrade list.
Content collections: Astro has them, Next.js does not
This is where Astro earned its keep. The Outpost defines its guides in src/content.config.ts with a glob() loader and a Zod schema. Every page defaults to status: 'draft', and a superRefine refuses to build a page marked approved unless its qc array holds a passing fact-check and a passing editorial review:
// powerlodge-pseo/src/content.config.ts (excerpt)
.superRefine((d, ctx) => {
if (d.status !== 'approved') return;
const passed = (stage: 'fact-check' | 'editorial') => d.qc.some((q) => q.stage === stage && q.verdict === 'pass');
if (!passed('fact-check') || !passed('editorial')) {
ctx.addIssue({
code: 'custom',
message:
'status: approved requires qc entries with verdict: pass for BOTH fact-check and editorial (AGENTS.md rule 1, amended 2026-09-02)',
});
}
if (d.qc.some((q) => q.verdict === 'fail')) {
ctx.addIssue({ code: 'custom', message: 'status: approved but a qc entry has verdict: fail — fix the page and re-run QC' });
}
})
That rule is why a machine-drafted page cannot reach production by accident. The workflow behind it is its own piece: the editorial quality gate behind The Outpost.
Next.js has nothing equivalent. This site's lib/blog.ts reads content/blog/*.mdx with gray-matter, hides drafts unless SHOW_DRAFTS=1, and gives the sitemap and RSS feed a separate function that only returns approved posts. The frontmatter check is a script I run by hand, and it does not block the build. It works. It is also two places where Astro has one.
Images and the cost of an image service
Astro's <Image> runs sharp at build and sets width and height automatically so nothing shifts. We did not use it. The Outpost mirrors dealer photos with its own sharp scripts, serves plain <img> tags with width, height, loading="lazy", and decoding="async" on cards, and puts fetchpriority="high" plus a <link rel="preload"> on heroes from src/layouts/Base.astro. No image service, no quota. This site uses next/image with AVIF and WebP, which is right for an image-heavy portfolio, but the sources still matter: capping every file at 1,600 px with sharp took the image set from 216 MB to 42 MB, and Vercel meters transformations. The image case study has the numbers.
Where does each one host, and what does it cost?
The Outpost deploys from GitHub Actions: Node 22, npm ci, mirror images, build, gate, then wrangler pages deploy dist. As of September 2026, Cloudflare Pages on the free plan allows 500 builds a month, one at a time, with a 20-minute timeout, 20,000 files, and 25 MiB per file, and static requests are free without limit.
Two things about Cloudflare before you copy this. First, the Astro Cloudflare adapter now targets Workers and no longer supports Pages for server-rendered routes. We chose Pages anyway: the dealership keeps its DNS zone with another provider, and a Pages custom domain on external DNS verifies with one CNAME, while a Workers custom domain needs an active zone in our Cloudflare account. Second, Pages serves the most recently created deployment, not the newest commit. On 2026-09-04 three overlapping CI runs finished out of order and production showed the oldest one. The fix was a concurrency group with cancel-in-progress: false, so runs queue.
This site deploys through Vercel's Git integration, and the gate runs inside npm run build. Vercel's Hobby plan is non-commercial only, and its fair-use page counts "receiving payment to create, update, or host the site" as commercial, so a client or studio site belongs on Pro, which as of September 2026 is $20 per seat per month with a $20 usage credit. That is the hosting delta for a commercial site: roughly $0 against roughly $20 a month, before image transformations.
What happens when the site needs to do something dynamic?
Then you are on Next.js, or you are bolting a service onto Astro. This site's contact and intake forms post to API routes, the CRM lives at /admin on Supabase, and an MCP endpoint lets an assistant read it. Next.js keeps all of it in one repo and one deploy. The Outpost's only dynamic need is a lead form, and that is written as a separate Cloudflare Worker (workers/lead.mjs) the form posts to, deployed on its own outside the Pages project. Fine for one form. Miserable for a dozen.
The same quality gate on both
The Outpost's scripts/gate.mjs runs 10 checks over dist/, from thin pages and near-duplicates above 0.85 cosine similarity to draft leakage in production and a robots inversion check between staging and production. Production fails on any failure, with no bypass flag, by design. The Next.js port reads .next/server/app, drops the inventory and staging-versus-production robots checks, and adds a noindex-versus-sitemap check plus a test that robots.txt and the sitemap were emitted. Its first green run covered 23 pages with zero failures.
A gate only needs HTML on disk, so the framework does not matter to it. The Next.js SEO checklist lists what it enforces, and it is what I set up under technical services for sites I did not build.
Which one for which business
| Project type | Pick | Why |
|---|---|---|
| Blog, docs, or marketing site | Astro | Known at build; free static hosting. |
| Programmatic SEO publication from a data feed | Astro | Build-time schema, committed snapshot, experimental incremental builds since 7.2. |
| Image-heavy portfolio, nothing else | Astro | Pre-size with sharp; no image service to meter. |
| Studio or agency site with forms, payments, and an admin | Next.js | The API routes and the pages share one deploy. |
| SaaS, dashboards, anything with a login | Next.js | Sessions and an API in one place. |
| Headless e-commerce front end | Next.js | Only once you have outgrown the platform's own theme. |
| Local business site, three to eight pages | Either; I lean Astro | A Quick Launch site is mostly pages; Next.js if a portal is coming. |
Build speed did not decide any of it. Vercel's own comparison page says Astro ships less client JavaScript on average and recommends it for blogs, docs, marketing sites, and portfolios, keeping Next.js for SaaS, e-commerce, and authenticated apps. The build-time benchmarks I have seen are split, and none has been re-run on Astro 7 or Next 16.3. What decided it, both times, was the second half of the sentence: a publication that needs no server, and a studio site that is also the business's back office.
Bottom line
Astro for a site that is mostly pages: blogs, docs, marketing, portfolios, and programmatic SEO. Next.js when the site is also an application: forms with logic, payments, an admin, an API. If you are not sure which yours is, it is mostly pages.
Not sure which one your site is?
Send me the URL and what it has to do. I will tell you which framework and what it costs to host.
Start a conversationQuestions people ask
- Is Astro or Next.js better for SEO in 2026?
- Neither wins on SEO by itself. Both can emit canonical tags, a sitemap, JSON-LD, and Open Graph images at build time. Astro ships less client JavaScript by default, which helps Core Web Vitals on content pages. Next.js needs discipline around client components and the Metadata API. The quality gate you run on the output matters more than the framework.
- Can Astro handle a site with forms, payments, or a login?
- Only with a server-side rendering adapter, and on Cloudflare that adapter now targets Workers rather than Pages. One form can live in a separate Worker or a form service. If the site has an admin, payments, or an API, Next.js keeps all of it in one codebase and one deploy.
- Which Astro and Next.js versions are current in September 2026?
- As of 2026-09-22, Astro 7.3.4 is current and needs Node 22.12 or newer. Next.js 16.3.6 is the Active LTS release and 15.5.26 is Maintenance LTS; both shipped that day as a security patch. Next.js 15 maintenance ends on 2026-10-21.
- Is Cloudflare Pages still safe to use for a new Astro site?
- For a fully prerendered site, yes. Cloudflare has not deprecated Pages, but the Astro Cloudflare adapter no longer supports it for server-rendered routes. Build static on Pages if DNS or account ownership favors it, and plan on Workers the day you need request-time rendering.
- Does Next.js 16 break a Next.js 15 site?
- Not automatically, but a custom webpack function in next.config makes the build fail unless you pass the webpack flag, synchronous params and searchParams are gone, and middleware moves to a proxy file. Read the upgrade guide before bumping.
Sources
- https://nextjs.org/docs/app/guides/upgrading/version-16
- https://nextjs.org/support-policy
- https://astro.build/blog/astro-7/
- https://docs.astro.build/en/guides/integrations-guide/cloudflare/
- https://developers.cloudflare.com/pages/platform/limits/
- https://developers.cloudflare.com/pages/functions/pricing/
- https://vercel.com/docs/limits/fair-use-guidelines
- https://vercel.com/pricing