DEV Community

Cover image for Astro Is Built for Thousands of Static Pages. So Why Does Nobody Talk About It That Way?
Rachid
Rachid

Posted on

Astro Is Built for Thousands of Static Pages. So Why Does Nobody Talk About It That Way?

Most Astro content I come across falls into one of two buckets: "here's a nice website I built" or "here's how Astro compares to Next.js for a marketing page." Both fair, both true. But the site I work on doesn't look like either of those, and it's the use case I almost never see written about: hundreds of near-identical, dynamically generated pages, all needing to rank in search, all needing to load fast, in two languages and two markets.

Concretely: a home-services marketplace with city pages, service pages, and city × service combination page — the kind of programmatic SEO structure where the page count isn't in the dozens, it's in the hundreds today and headed toward tens of thouisands as the underlying data grows. That's a genuinely different problem than "make one fast landing page," and it turns out Astro is unusually well suited to it — better suited, in some concrete ways, than the frameworks that get all the programmatic-SEO blog posts.

The default that actually matters: zero JS unless you ask for it

Every "why Astro" post mentions the islands architecture. What gets undersold is what that means specifically for a large templated site: a city × service page — heading, worker cards, an FAQ block, some internal links — ships as plain HTML with no JavaScript bundle at all, by default. Not "a smaller bundle." None.

Compare that to a static export from a React-based meta-framework, where every page is still a component tree meant to hydrate, so you're shipping (and executing) framework runtime JS even for a page that never needs to do anything client-side. At one page, that's not a big deal. At a few hundred templated pages, each with real content and real images, it's the difference between a site that stays fast as it grows and one that needs constant bundle-splitting effort just to stand still.

In Astro, the handful of things that genuinely need JS — a filter widget, a search box — opt in explicitly, and you choose when:

astro

Everything else on the page is never shipped as JS at all, because it was never a component that needed to run in the browser — it just needed to render once, at build time.

Static generation that scales past "a few pages"

getStaticPaths() is the other underrated piece. It's not fundamentally different from what other SSGs offer, but in practice, generating a page per city, per service, and per city×service combination from plain data (JSON, a CMS, eventually a database) is exactly the shape of problem Astro's routing was designed around — and it stays boring at scale, which is what you want from build tooling. Add a new city to your data source, and the page exists at the next build with no template changes.

The place this gets interesting is exactly where it gets hard: once your data source stops being "a hundred hand-checked records" and starts being "tens of thousands of records from a real backend," you have to get deliberate about what actually deserves a static page versus what should be batched, tiered, or deferred — completeness of the underlying data becomes a real input into your routing strategy, not just an afterthought. That's less of an Astro-specific lesson and more a reminder that static-site scale problems are data problems first, framework problems second.

The sharp edge nobody warns you about

I wrote up one of these in detail recently: Astro's component can't gracefully handle a dead remote URL, because the actual network fetch happens inside an internal step that isn't part of the pluggable Image Service API — so one broken image URL in your dataset can take down an entire production build. Not a dealbreaker, but a real gap between "framework magic" and "framework you should validate your inputs before trusting."

The fix ended up being a small prebuild script that checks every remote image URL's reachability before Astro ever touches it, with a fallback UI for anything that fails the check. Worth knowing about before it takes down a deploy at 6pm on a Friday, not after.

Where it's genuinely not the right tool

To be fair to the frameworks that do get all the blog posts: Astro is not the right choice for the actual application layer. Our app itself is a separate native app; the Astro site is purely the public-facing, SEO-critical surface — city pages, service pages, profile pages. If you need authenticated dashboards, real-time state, or heavy client-side interactivity as the core product, reach for something built around that, not a static-first framework. Astro plays well as the SEO/marketing front door for a product that lives somewhere else, which is exactly our setup.

Why the silence, then?

My honest guess: programmatic SEO at real scale is a less glamorous problem than "look at this beautiful blog," so it doesn't get the same content. But if you're building a site where the page count is the whole point — city pages, product category pages, location pages, anything templated across a large dataset — the zero-JS-by-default model is doing more work for you than it gets credit for.

We run this pattern on bricoleapp.com, a bilingual marketplace across two North African markets — happy to go deeper on the pSEO/data side if that's useful to anyone hitting the same wall.

Top comments (0)