Free AI Opportunity Audit

Astro vs Next.js for a CMS Migration: What Actually Decides It

Shyam Verma
Astro vs Next.js for a CMS Migration: What Actually Decides It

Short answer: if your site is pages, pick Astro. If your site has an account behind it, pick Next.js. The framework you land on barely moves the migration bill, so choose it on what you will still be running in three years, not on what quotes cheapest this quarter.

I should declare an interest before you read another line. We build on Next.js. This site is Next.js. Recommending Astro costs me work, which is roughly the only reason a paragraph like that one is worth reading.

Does the destination framework change what the migration costs?

Barely. That surprises people, and it is the most useful thing in this post.

Here are the bands we publish, unchanged, for Joomla, WordPress and Drupal. The dollars are the hours at $100/hour, a round number chosen so the arithmetic stays visible rather than a rate card. Substitute your own market rate and the hour bands still hold.

What you have Engineering hours At $100/hr Calendar
Brochure site, under 50 pages, no logins 80–160 $8,000–16,000 3–6 weeks
Content site, 200–2,000 entries, custom fields 200–450 $20,000–45,000 8–16 weeks
Commerce or membership site with real accounts 450–900 $45,000–90,000 4–8 months
Multisite or multilingual estate 900+ $90,000+ 6–12 months

Notice what is not in that table: the destination. The work that fills those hours is auditing what your old CMS is actually doing, getting the content out in a shape you can trust, rebuilding the front end, and keeping every URL answering. Three of those four are identical whichever framework you land on, and the fourth — rebuilding the front end — differs by maybe ten to fifteen per cent of its own line, not by a band.

So if someone quotes you meaningfully less because they are proposing Astro, ask what they took out. Usually it is the redirect map.

What moves you between bands is on the source side — plugin count, page builders, custom modules, commerce behaviour, multilingual bookkeeping. Those three posts above cover it per CMS, and that is the reading to do before you argue about frameworks. As for how we scope it: an audit first, then a fixed-quote pilot on one real section taken end to end, because the honest answer to "what will this cost" is usually "one section will tell us."

What Astro and Next.js actually disagree about

Strip the marketing and there is one disagreement. Both frameworks render on the server by default. They differ on what happens to the JavaScript afterwards.

Astro's documentation describes an island as "an enhanced UI component on an otherwise static page of HTML," and is blunt about the default: "By default, Astro will automatically render every UI component to just HTML & CSS, stripping out all client-side JavaScript automatically." Interactivity is opt-in, one component at a time, with a client:* directive. The docs list what those directives mean, and they are worth reading as a menu rather than a syntax: client:load hydrates immediately on page load, client:idle waits for requestIdleCallback, client:visible waits until the component enters the viewport, client:media waits for a CSS media query, and client:only skips server rendering entirely and renders on the client. Five different answers to "when does this cost the user anything."

Next.js starts from the other end of the same idea. In the App Router, per its docs, "by default, layouts and pages are Server Components," and you opt into the client with use client. The mechanism is a module boundary rather than a per-instance directive, and that is the difference that bites: "Once a file is marked with use client, all of its imports and the components it directly renders are included in the client bundle."

Read those two sentences next to each other and you have the whole comparison. Astro makes the client bundle something you add to deliberately, component by component, with an explicit note about when. Next.js makes it something you draw a line around, and everything inside the line comes along whether you meant it or not.

Neither is better. They are different failure modes: Astro's is forgetting a directive and shipping something inert, Next.js's is one careless use client on a shared file dragging a component library into every page.

The second disagreement is about the server, and it is the one that decides your hosting bill. Astro's docs: "By default, your entire Astro site will be prerendered, and static HTML pages will be sent to the browser," and "to render any page on demand, you need to add an adapter" — where "each adapter allows Astro to output a script that runs your project on a specific runtime." You then opt individual routes out with export const prerender = false. Static is the floor; the server is something you add.

Next.js assumes the server and lets you take it away. Which is why its static export mode comes with a list of things that stop working, and why that list is the most important page in this entire comparison. More on it below.

Astro's own positioning is worth quoting: "Astro is the web framework for building content-driven websites like blogs, marketing, and e-commerce." The same page describes what it sees other frameworks as being for — "logged-in admin dashboards, inboxes, social networks, todo lists" — without claiming Astro cannot do those. That is a statement of focus, and taking it at face value will serve you better than any benchmark.

The five questions that decide it

Not "which is faster." These.

Question If yes Why it decides anything
Is there a logged-in area with real session state? Next.js Accounts, carts, dashboards and personalised views are the app-shaped work Astro's own docs describe other frameworks as being designed for. Astro's docs do say it can scale up to dynamic applications — but that is its stretch case, and Next.js's home ground.
Do you publish more than a few hundred pages, edited daily? Next.js ISR exists precisely to "handle large amounts of content pages without long next build times". Astro's documented modes are prerender-at-build or render-on-demand.
Is the site essentially a brochure, a blog, or a marketing estate? Astro It is the case the framework is built and documented for, and the default output is HTML with no JavaScript attached.
Does your team already own a React component library? Next.js Astro runs React fine, but per-component, inside .astro files, with a hydration decision each time. A shared React design system is a Next.js asset and an Astro chore.
Do you need it hosted somewhere unusual — your own VPS, a client's IIS box, a locked-down CDN with no runtime? Astro Prerendered is the default and needs no adapter. Next.js can static-export, but read the next section before you promise it.

Two or three yeses on one side and you are done. A genuine split usually means the site is really two sites — a marketing estate and an application — and the honest answer is two deployments on one domain, not one framework being mediocre at both.

What actually breaks

These are the eight things I have seen turn a framework choice into a change order. Four apply to Astro, four to Next.js.

1. Astro's redirects are not HTTP redirects in a static build. This is the big one, and it is a migration-specific trap that nothing else in this comparison comes close to. Astro lets you declare redirects in astro.config, and "the status code is 301 by default." But the docs state what actually gets emitted: "When running astro build, Astro will output HTML files with the meta refresh tag by default. Supported adapters will instead write out the host's configuration file with the redirects." A meta refresh tag is an HTML page that tells the browser to go somewhere else. It is not a 301. On a migration where a decade of inbound links and Search Console history is the single most valuable thing you own, quietly converting your entire redirect map into HTML pages is not a detail. Either use an adapter that writes real host configuration, or write the redirect rules directly in your host's config and treat the Astro redirects key as documentation.

2. Next.js permanent: true emits a 308, not a 301. People write permanent: true in next.config.ts, expect a 301, and then a validation tool flags "unexpected redirect type" and everyone panics on launch day. The docs are explicit: true uses 308, false uses 307. Next.js chose those two because, unlike 301 and 302, they preserve the request method. Search engines treat 308 as permanent. It is fine. Know it before the launch call, not during it.

Here is the same three-rule map on each side, which is the fastest way to see the shape of the difference:

// next.config.ts — emits real 308s from the Node server or the edge.
// Pattern matching does the heavy lifting: this file has 112 rules on
// readybytes.in and the patterns replace thousands of enumerated URLs.
export default {
  async redirects() {
    return [
      { source: '/old-blog/:slug', destination: '/blog/:slug', permanent: true },
      { source: '/category/:term/page/:n', destination: '/blog', permanent: true },
    ]
  },
}

// astro.config.mjs — 301 by default, BUT in a plain static build these
// become HTML pages carrying a meta refresh tag, not HTTP responses.
// With a supported adapter they are written into the host's own config.
// On a migration, verify which of those two you actually got.
export default {
  redirects: {
    '/old-blog/[slug]': '/blog/[slug]',
    '/pricing.html': { status: 301, destination: '/services' },
  },
}

3. output: 'export' in Next.js turns off more than anyone expects. "We will just static-export it and drop it on a CDN" is a sentence that has ended several migration budgets. The docs are unambiguous that features requiring a Node.js server "are not supported", and the list includes, among others, redirects, rewrites, headers, Proxy, Incremental Static Regeneration, image optimization with the default loader, Draft Mode and Server Actions. Read that list again with a migration in your head. Redirects and rewrites gone means your URL map has to move into nginx or your CDN. Draft Mode gone means your editors lose preview. Those are not nice-to-haves you discover in month three; they are two of the four things a migration is for.

4. ISR only exists if you have a Node runtime. Two sentences from the ISR docs settle the architecture: "ISR is only supported when using the Node.js runtime (default)," and "ISR is not supported when creating a Static Export." The platform support table says the same thing colder — Node.js server yes, Docker yes, static export no. So if you have 4,000 posts and your plan is a static export to object storage, every typo correction is a full site build. And if you self-host across multiple instances, read the caveat nobody reads: "When running multiple instances, the default file-system cache is per-instance. On-demand revalidation only invalidates the instance that receives the call." One container gets the fresh page. The others keep serving yesterday. That is a shared cache handler you now have to configure, and it belongs in the estimate.

5. Astro's adapter is a hosting decision you make on day one. Anything rendered on demand needs an adapter, and an adapter targets a specific runtime. Server islands — the server:defer mechanism Astro documents for moving "expensive or slow server-side code out of the way of the main rendering process" — need one too. Adapters are swappable in principle. In practice the caching model you build on top is not, because it is the host's. The Netlify adapter, for instance, documents a cacheOnDemandPages option that will "cache all server-rendered pages for up to one year," plus CDN-Cache-Control headers for per-page control. That is CDN caching, and it is a genuinely good answer — but it is a different mental model from route-level revalidation, and porting between hosts means rebuilding it.

6. use client creep is the Next.js equivalent of a leaked dependency. One directive at the top of a shared utility, and every one of its imports lands in the client bundle. On a migration this happens for a boring reason: you port a component from the old theme, it uses a browser API, you add use client to make the error go away, and six weeks later the marketing pages are shipping a chart library. The fix is in the docs — push the directive down to the smallest interactive leaf — but nobody does it retroactively, so build the habit during the migration, when the components are new and cheap to move.

7. Multilingual: neither framework translates anything, but they hand you different amounts of scaffolding. Astro has i18n routing in core — locales, defaultLocale, prefixDefaultLocale, fallback, and helpers in the astro:i18n module — and the docs are careful about scope: "This routing API helps you generate, use, and verify the URLs that your multi-language site produces." URLs, not content. Next.js's App Router gives you less: its own redirects documentation says locales in next.config.js work "only as hardcoded paths", and points you at dynamic route segments plus Proxy for anything per-request. Neither one solves the actual multilingual migration problem, which is that the join between a page and its translations lives in your old CMS's plugin tables. That is the source-side work the sibling posts cover. But if you are building locale routing from scratch, Astro hands you more of it.

8. A React component library does not port to Astro for free. Astro has official integrations for React, Preact, Svelte, Vue, SolidJS and AlpineJS, and you can put several of them on one page — with a rule worth knowing before you plan the work: "Only Astro components (.astro) can contain components from multiple frameworks," and framework component files themselves cannot mix frameworks. So your React components run, but they run as leaves inside .astro pages, each with a hydration decision attached. If you have a mature design system, that is a per-component review across the whole library. If you have twelve components, it is a Tuesday.

Where each one is obviously right

Astro, when the site is pages. Marketing estates, documentation, publications, brochure sites, a blog with a few hundred posts, anything where the ideal amount of JavaScript is close to zero and the interactive parts are a search box and a menu. Content collections give you typed content with Zod schema validation and loaders that pull from files or from wherever else your content lives, which is a real content model rather than a folder of Markdown you hope is consistent. For that shape of site the default output — HTML, no JavaScript attached — is what you actually wanted, and you spend the project adding interactivity rather than removing it.

Next.js, when there is an application in there. Accounts, carts, dashboards, gated content, anything with per-user state, and any content estate large enough that rebuilding the whole site to fix a paragraph is absurd. ISR is the specific reason: publish, call revalidatePath, that route regenerates, nothing else rebuilds. On a 2,000-page site that is the difference between a publishing workflow and a deployment ceremony.

And there is a boring institutional argument that deserves saying out loud, because it decides more real projects than architecture does: hiring. React developers are easier to find than Astro developers, and the person who inherits this site in 2029 is a real constraint. That cuts toward Next.js, and it cuts hardest for the client who will not have a retained agency. It is a legitimate tiebreaker and I would rather you hear me use it than pretend the decision is purely technical.

Who should not be choosing between these at all

  • Your site is fine and your complaint is speed. "The site is slow" and "the CMS is wrong" are different diagnoses. A better host, a caching layer and an image diet are a fortnight, not a quarter. Measure before you replatform.
  • Nobody on your side runs a build pipeline. Both frameworks compile. Both need a deploy step, a Node version, and someone to look at a failing build. A site you cannot deploy is worse than an old site you can.
  • Your pain is one feature. A booking system, a broken form, a search that does not work. Replace the feature. Replacing the platform to fix a form is the most expensive way to fix a form.
  • You have not inventoried your URLs. Do that first. It is a day of work and it changes the shape of every quote you will receive, including ours.
  • You are inside a revenue-critical season. Migrate in your quiet quarter. Nothing about this is urgent enough to do in November.

And whichever way you go, do not do it in one cutover. Keep the old CMS running and put the new site in front of it, section by section — the reverse-proxy approach — which turns one terrifying launch into a series of small reversible ones. It works the same for either framework.

Honest limits. I have not shipped a client site on Astro, and I am not going to imply otherwise. Everything above about Astro comes from its documentation, read today. What I have done is the migration itself, repeatedly. Our production work is WordPress to Next.js and Directus: four properties consolidated into one codebase for ReelAbilities, fifteen city festival sites running on it since, with the twelve-month engineering teardown — including what went badly — written up here. This site is Next.js 16 with 112 redirect rules in next.config.ts, migrated off a PHP CMS, and the rules are in the repository rather than on a slide. Ready Bytes started in 2009 building CMS extensions that ran on 10,000+ websites within two years, then a recurring-billing suite on 40,000+ sites before we sold its IP in 2017. Those installs are the legacy sites people are migrating now.

If your project is squarely in Astro's lane and you want that, say so on the call. I would rather lose the build than deliver you a heavier one.

Common questions

Is Astro cheaper to migrate to than Next.js?
Not materially. The cost of a migration lives in the source CMS — plugins, page builders, custom modules, commerce behaviour, the URL inventory — and none of that changes based on where you land. If a quote is much lower because it names Astro, ask which line was removed.

Can I use my React components in Astro?
Yes, through the official React integration. They run as components inside .astro files, and each interactive one needs a client:* directive deciding when it hydrates. Astro's docs also note that only .astro files can host components from multiple frameworks. So a design system ports, but as a per-component review rather than a copy.

Does Astro have an equivalent of ISR?
Not as a documented framework feature with that shape. Astro's documented modes are prerender at build time or render on demand via an adapter, with page caching handled by the host — the Netlify adapter, for example, documents a cacheOnDemandPages option that caches server-rendered pages. That solves the same problem from a different layer. If your requirement is "regenerate exactly this one route when an editor hits publish," Next.js's revalidatePath is the direct answer.

Can I run Next.js without a Node server?
Yes, with output: 'export' — and the docs list what stops working, including redirects, rewrites, headers, Proxy, ISR, Draft Mode, Server Actions and image optimization with the default loader. On a migration, redirects and preview are two of the things you most need. Static export is a real option for a small brochure site and a trap for anything else.

Will I lose my Google rankings?
Only if old URLs stop answering, and the framework is not what decides that. Baseline Search Console before cutover, map every URL, and judge at eight weeks rather than eight days. On Astro, verify that your redirects came out as real HTTP responses and not meta refresh pages — that check alone is worth an hour of somebody's time.

What is the smallest sensible first step?
Inventory your URLs and your content types. Then take one section end to end before committing to the whole site. That is what a fixed-quote pilot is for, and it is a reasonable thing to ask of any agency, including ours. If you are still choosing who to ask, we compared seven legacy modernization and AI automation agencies, ourselves included.


Shyam Verma founded Ready Bytes in 2009 and spent its first decade building products for the PHP CMS ecosystem. He writes about migrations, legacy modernization and applied AI. Got a replatform quote and cannot tell whether the framework choice in it was reasoned or reflexive? Send it over — including the quotes that are not ours.

Shyam Verma

Shyam Verma

Full Stack Developer & Founder

Shyam Verma is a seasoned full stack developer and the founder of Ready Bytes Software Labs. With over 13 years of experience in software development, he specializes in building scalable web applications using modern technologies like React, Next.js, Node.js, and cloud platforms. His passion for technology extends beyond coding—he's committed to sharing knowledge through blog posts, mentoring junior developers, and contributing to open-source projects.

Comments