Astro in production: what we learned shipping content sites on it

Candid notes from shipping client sites on Astro + Cloudflare: content collections, islands, build-vs-runtime tradeoffs, and the sharp edges nobody mentions.

We’ve written before about why our stack is Astro + Tailwind + Cloudflare and why Cloudflare specifically. Those posts are the sales pitch, more or less. This one is the engineering debrief: what actually happened after we committed to Astro for real client work, including the site you’re reading right now.

Short version: the bet paid off. Longer version: it paid off for specific reasons that only apply to specific kinds of projects, and there are sharp edges we hit that the framework’s marketing won’t tell you about. If you’re an owner deciding what your next site should be built on — or a developer deciding whether to trust the hype — the details below are the honest picture.

The core idea, and why it fits client work

Astro’s premise is that a website is mostly content, and content should ship as HTML. Not a JavaScript application that renders HTML after it loads — actual HTML, generated once at build time, served from a CDN.

That sounds like a throwback to 2005, and in a sense it is. The difference is that you write the site with modern tooling — components, TypeScript, a real build pipeline — and the output is old-fashioned static files. You get the developer experience of a modern framework and the delivery characteristics of a folder of HTML.

For the work we do — marketing sites, service pages, local landing pages, this blog — that’s almost exactly the right trade. A visitor from Lincoln searching for a web developer doesn’t care about our build pipeline. They care that the page appears instantly on their phone. Static HTML from an edge node a few milliseconds away is the most reliable way we know to make that happen, and it doesn’t degrade when a third-party script has a bad day.

Content collections earn their keep

The single most valuable Astro feature in production, for us, isn’t performance. It’s content collections.

A content collection is a folder of content — blog posts, city pages, case studies — with a schema attached. Every entry must have a title, a date, a description of the right shape, the right category from an allowed list. If a post is missing its meta description, the build fails. Nothing ships.

That sounds bureaucratic until you’ve run a site with 30+ pages for a while. Without enforcement, content drifts: someone forgets a description, a date gets typed in the wrong format, a tag gets spelled three ways. Each mistake is invisible until it costs you — usually in search, weeks later, where you can’t see the error directly. With collections, the whole class of problem is gone. The site literally cannot build in an inconsistent state.

The second-order benefit: because content is typed data, everything downstream is generated instead of maintained. Our sitemap, our RSS feed, our llms.txt file for AI crawlers, the related-posts links, the FAQ structured data on every post — all of it is derived from the collections at build time. When we add a page, six other things update themselves. On a hand-maintained site, those six things are six chances to forget.

Islands: JavaScript on purpose, not by default

Astro’s other headline feature is the islands architecture. The page is static HTML; individual components can opt in to being interactive “islands” that load their own JavaScript.

In practice, this changed how we think about interactivity. On an app framework, everything is JavaScript, so adding one more interactive widget is free — you’re already paying for the runtime. On Astro, each island is a visible, deliberate cost. You find yourself asking: does this actually need to be a React component, or is it a <details> element? Does this need to hydrate on page load, or only when it scrolls into view?

Our rough decision ladder, hardened by a couple dozen real decisions:

  • Plain HTML and CSS first. Accordions, tabs-that-are-really-links, hover states, simple menus. Modern HTML covers more than most developers assume.
  • A small vanilla script second. A copy-to-clipboard button or a mobile nav toggle doesn’t justify a framework. Twenty lines of script, no dependency, nothing to hydrate.
  • A React island third, and only when there’s real state to manage. Our in-page product demos and the “Can AI help?” widget on /ai are React, because they hold conversation state, talk to a backend, and re-render meaningfully. That’s what the framework is for.

The result on this site: most pages ship effectively zero JavaScript, and the pages that do ship it, ship only the component that needs it. Nobody downloads chat-widget code on a page with no chat widget.

Build time vs. runtime: where each one belongs

The most useful mental model we’ve settled on: decide at build time everything you can, and push to runtime only what you must.

Concern Where it runs Why
Pages, posts, city pages Build time Content changes rarely; HTML is fastest and can’t error at 2 AM
Sitemap, RSS, structured data Build time Derived from content; regenerate on every build
Contact form handling Runtime (Worker) Needs to send email and write to a database
Chat / AI scoping widget Runtime (Worker + D1) Genuinely dynamic conversation, per-visitor state
Analytics events Runtime (Worker + D1) Data arrives from visitors; can’t exist at build time

The static side deploys as files on Cloudflare Pages. The dynamic side runs as Workers with D1 behind them. The boundary is explicit, which is the point: everything on the left of that line has no server to crash, no dependency to patch, no cold start. Only the right side needs monitoring, and it’s a small, well-fenced surface.

One honest consequence of static-first: updating content requires a rebuild. If a post is scheduled to publish Tuesday, the HTML that includes it doesn’t exist until something triggers a build on Tuesday. We handle that with scheduled builds, but it’s a real operational detail that server-rendered sites don’t have. If your site changes many times a day — inventory, pricing, listings — static-first is the wrong default and you should scope that honestly up front.

The sharp edges we actually hit

Every one of these cost us a real debugging session, so you don’t have to trust the brochure:

  • Scoped styles are scoped harder than you expect. Astro scopes a component’s CSS to that component’s own markup. HTML that arrives any other way — rendered from Markdown, injected by a script — doesn’t get the scoping attributes, so your styles silently don’t apply. The fix (is:global, or styling from a layout level) is easy; diagnosing it the first time is not, because nothing errors. The page just looks subtly wrong.
  • Inline scripts and processed scripts behave differently. Astro bundles and can hoist your script tags, which changes when they run relative to the DOM. A getElementById that worked in one component can return null after a refactor moves the element. The lesson: treat script/DOM timing as something to verify, not assume.
  • Props crossing into islands must be serializable. The static side and the interactive side are genuinely separate worlds; you can’t pass a function or a live object into an island. You feel this the first time you try to hand a callback to a React component and get a cryptic error. It’s a fair constraint — it’s why the architecture works — but it shapes your component design.
  • A clean build is not a correct page. This one is on us, not Astro, but static generation makes it seductive: the build passes, so ship it. We’ve been burned by pages that built fine and rendered wrong. House policy now is to verify rendered output — actual screenshots of the worst-case pages — before anything deploys, the same way tests are non-negotiable on the code itself.

None of these are disqualifying. All of them are the kind of thing you only learn by shipping, which is roughly the thesis of this post.

When we don’t reach for Astro

Being candid about the boundaries:

  • Real applications. If users log in and work in the thing — dashboards, portals, internal tools — we build a React app, sometimes with no Astro involved at all. When 90% of the page is interactive, islands stop being an optimization and start being ceremony.
  • Owner-edited content. Astro content lives in files in a repository. That’s a feature for us (versioned, reviewable, schema-checked) and a wall for a client who wants to edit their own pages. That’s solvable with a CMS in front, but it’s added scope and we say so in the quote rather than discovering it in week six.
  • Sites that change constantly. As above — if your content changes hourly, the rebuild model is friction. Runtime rendering exists for a reason.

You probably don’t need this framework specifically, either. A five-page site that changes once a year would be fine as plain HTML. What you need is a builder who picks the boring, fast option on purpose — Astro happens to be our current version of that for content sites, and we’ll swap it the day something serves clients better.

What this means if you’re buying, not building

If you’re a business owner rather than a developer, the takeaways compress to three things:

  1. Ask what the site ships to the browser. “Modern framework” can mean a fast static page or a half-megabyte JavaScript bundle pretending to be one. The difference shows up in your mobile load times and your search rankings.
  2. Ask what happens when content is wrong or missing. A build that refuses to ship an inconsistent site is worth real money over the life of the site. Hand-maintained consistency always drifts.
  3. Ask what the ongoing hosting and maintenance actually costs. Done right, a content site in 2026 should cost almost nothing to host and very little to keep alive. Recurring fees should map to actual ongoing work, not to keeping a fragile stack upright.

This is the stack behind our own work, and marketing sites built this way land in the $8K–$25K range at flat-bid pricing — the number is quoted before work starts, and the process includes the rendered-output verification and testing described above. If you’re weighing a rebuild and want a straight answer on whether this approach fits your situation — including “your current site is fine, keep it” — get in touch. We’re at Stanford Ranch in Roseville, and the first conversation costs nothing.

Related reading: our modern web stack · why we build on Cloudflare · how a boutique studio tests software · what web budgets actually buy in Roseville

FAQ

Frequently asked questions.

The questions clients ask most after reading this.

What is Astro, in one paragraph?

Astro is a web framework built around a simple idea: most pages on most sites are content, and content doesn't need JavaScript to render. Astro compiles your pages to plain HTML at build time and ships zero JavaScript by default. When a specific component genuinely needs interactivity — a search box, a chat widget — you opt that one component in. Everything else stays static, fast, and cheap to serve.

Is Astro good for a small-business marketing site?

It's one of the best current options. Marketing sites are mostly text, images, and forms — exactly what Astro is built for. Pages arrive as pre-rendered HTML, so they load fast on phones and score well on Core Web Vitals, which matters for search. The main caveat: Astro is a developer tool, not a page builder. If you want to edit pages yourself without a developer, you'll want a CMS wired in front of it, which adds scope.

When is Astro the wrong choice?

When the thing you're building is an application rather than a site. Dashboards, portals, anything where the user logs in and works inside the product for twenty minutes — those want an app framework where the whole page is interactive by design. Astro can host islands of interactivity, but if 90% of the screen is interactive, the islands model is fighting you instead of helping. We build those as React apps instead.

What are Astro content collections and why do they matter?

Content collections are Astro's way of treating your content — blog posts, service pages, city pages — as typed data with an enforced schema. Every post must have a title, date, and description in the right format, or the build fails before anything ships. On a site with dozens of pages, that's the difference between catching a missing meta description at build time and discovering it in search results a month later.

Does Astro work with React?

Yes, and this is most of its appeal for us. Astro renders React components at build time and only sends their JavaScript to the browser when you explicitly mark them interactive. So we get React where it earns its cost — an interactive demo, a chat widget — and plain HTML everywhere else. One codebase, one component model, and the browser only pays for the parts that actually do something.

How does Astro compare to WordPress for performance?

A stock WordPress page is assembled by a PHP server on every request, then typically patched with caching plugins to make it acceptable. An Astro page is built once and served as static HTML from a CDN — there's no server to be slow. In our experience the practical gap shows up on mobile: static HTML from an edge node close to the visitor is consistently fast without the plugin stack, and there's dramatically less to maintain or get hacked.

What does an Astro site cost to host?

Very little. Static output on Cloudflare Pages serves from their free tier for most small-business traffic levels; even with dynamic pieces — form handling, a chat endpoint on Workers, a D1 database — the sites we run cost single-digit dollars a month, and often nothing. The build cost is where the money goes. Hosting a well-built static site is close to a solved, free problem in 2026.

Who helps small businesses near Rocklin and Roseville, CA build sites like this?

Grey Sky Media — a software studio founded in Rocklin in 1999, now in Roseville at 6770 Stanford Ranch Rd, with 187+ projects shipped. We build marketing sites on Astro + React on Cloudflare's edge, quoted flat ($8K–$25K typical, no hourly billing), with test-driven development as house policy. We serve Rocklin, Roseville, Auburn, Lincoln, Granite Bay, Folsom, and Sacramento, plus remote. Call (916) 234-0040 or start at /contact.

More development reading

Related from the lab.

All field notes

Can AI help?

What's the task your team does manually every day?

Tell us in plain words. We'll ask a couple of questions, then tell you honestly whether it's worth automating — no sales pitch.