Web Development

Why we're piloting Astro for our marketing site

DEV

Astro launched this year with a simple pitch: ship HTML by default and only hydrate the components that actually need JavaScript. For a marketing site that's roughly 90 percent static content and 10 percent interactive widgets, that pitch is hard to ignore, so we set aside two weeks to rebuild our own marketing site as a pilot and see how far the idea could take us in practice rather than in a demo.

Why the islands architecture appealed to us

Most static site generators force a binary choice: render everything as static HTML and hand-roll interactivity with vanilla JavaScript, or bring in a full client-side framework and accept the bundle-size cost even on pages that barely need it. Astro's islands model splits the difference. Individual components can opt into client-side JavaScript through a hydration directive, and everything else on the page ships as plain HTML with zero framework runtime attached. On our marketing site, that meant the pricing calculator and the newsletter signup form got JavaScript, while the hero section, testimonials, and footer did not, even though all three were built as components in the same codebase.

The hydration directives themselves took some getting used to. `client:load` hydrates immediately, `client:idle` waits for the browser to be free, and `client:visible` waits until the component scrolls into the viewport, which turned out to be the right choice for anything below the fold. Getting this wrong in either direction is easy: mark too many components `client:load` and you've quietly rebuilt the exact bundle-size problem Astro exists to solve, mark something interactive as static by accident and it just silently doesn't work, with no build error to point you at the mistake.

What the component model bought us

The framework-agnostic component model let us write in whichever framework already had the component we needed, which meant we could keep a handful of existing Vue components from our old site without a full rewrite, and mix in a couple of new components in plain vanilla JavaScript for the pieces that didn't need a framework at all. Build output for the full 40-page site was noticeably smaller than the equivalent Next.js static export we compared it against, mostly because unused component JavaScript never shipped to the browser in the first place rather than being tree-shaken after the fact.

  • The component model let us write in whichever framework already had the component we needed, so we kept several existing Vue components without a rewrite.
  • Build output for the 40-page site was smaller than the equivalent Next.js static export, since unused component JavaScript never ships to the browser at all.
  • Markdown and MDX support through Astro's content collections handled the blog section well enough to replace our headless CMS integration for that part of the site.
  • Per-page control over hydration meant heavy interactive pages loaded roughly the same amount of JavaScript as before, while static pages loaded almost none.

Where the tooling still showed its age

Astro's ecosystem is young, and it showed in a few specific places. Image handling, which we'd taken for granted after using the Next.js image component on other projects, required more manual work here; there's no equivalent built-in optimization pipeline yet, so we ended up running our own build-time image compression step alongside Astro rather than through it. A couple of third-party plugins we tried assumed a more mature integration surface than Astro currently exposes, and we ran into version-compatibility issues with a syntax-highlighting plugin that hadn't been updated since an earlier Astro release. None of these were blockers, but each one cost us debugging time we hadn't budgeted for a framework this new.

The developer experience around content collections and Markdown, on the other hand, was solid enough that we're seriously considering it as a lightweight CMS replacement for other content-heavy client sites, not just this pilot. Frontmatter-based content with TypeScript-checked schemas caught a couple of malformed blog post dates before they ever reached a build, which is the kind of small safety net that's easy to underrate until it saves you from a broken production deploy.

What the numbers actually looked like

We ran the same Lighthouse audit against the old site and the Astro rebuild on the five pages we get the most traffic on: the homepage, two product pages, the pricing page, and the top-performing blog post. Total JavaScript shipped to the browser dropped from around 180 kilobytes gzipped on the old Next.js version of the homepage to under 15 kilobytes on the Astro version, since the only client-side JavaScript on that page at all was the newsletter signup form's validation logic. Time to interactive dropped by more than a second on a throttled mobile connection, and the Lighthouse performance score moved from the low 80s into the high 90s across all five pages without any additional optimization work beyond the framework switch itself.

The blog post page told a slightly different story worth calling out, since it was the one page where Astro's win was smaller than we expected going in. Because the page was already mostly static text and a couple of images even under the old Next.js setup, the JavaScript savings were less dramatic there than on the interactive product pages; the real lesson was that Astro's biggest wins show up precisely where a site was over-serving JavaScript for content that never needed it, and a page that was already close to static doesn't have much room left to improve.

How the editorial workflow held up

Because our own marketing and content team is small, we didn't need to solve for a large non-technical editing workforce the way a client project might, but we still wanted the blog section to be editable without a developer's help for routine posts. Content collections, backed by Markdown files with a TypeScript-validated frontmatter schema, ended up being enough for our own team's needs, and the schema validation caught a mistyped publish date and a missing author field before either ever reached a deployed build. For a client with a larger non-technical content team, we'd likely still recommend pairing Astro with a proper headless CMS through its content collections API rather than asking editors to write Markdown directly, but for our own internal use the plain file-based approach removed a dependency rather than added one.

The rough edges that took the longest to work around

Beyond image handling, the two areas that cost us the most unplanned time were TypeScript support and the dev server's hot reload behavior on larger pages. TypeScript inside `.astro` files worked, but editor tooling lagged behind what we're used to with Vue or React single-file components; go-to-definition and inline type errors were inconsistent inside the frontmatter fence at the top of an Astro component, which meant catching type mistakes there leaned more on the build step failing than on the editor catching it as we typed. Hot reload also struggled on our longest page, the pricing page with a dozen or so components, where a single content change occasionally triggered a full page reload rather than the targeted update we expected, a rough edge we didn't hit on smaller pages and only noticed once we were deep into the pilot.

None of these were bad enough to abandon the pilot, but they're the kind of friction that's easy to underweight when you're evaluating a new tool for a day and easy to overweight once you're living with it for two weeks straight. We've since learned to budget extra review time for anything written inside an Astro frontmatter block specifically, since that's where the tooling gap showed up most consistently, and we'd tell another team evaluating Astro to run their pilot against their single most complex page rather than a handful of simple ones, since that's where the current rough edges actually surface.

What we'd tell another team considering this

Astro isn't ready to replace every project in our stack, and we wouldn't recommend it yet for anything heavily interactive, like an authenticated dashboard or a product with complex client-side state. But for content-first sites where performance is the whole pitch, and where the bulk of the page is genuinely static, it's the most compelling new tool we've tried this year, and it delivered a faster site with less shipped JavaScript than any of our existing approaches without asking us to abandon our current component libraries to get there. We'll be watching the 1.0 roadmap closely, particularly around image optimization and plugin stability, before recommending it for a paying client's production site.

← Back to the journal

Have a project in mind?
Let’s talk.

Tell us where you are and where you want to go. We'll map the fastest route between the two.

Currently accepting new clients