Why we picked Astro for our company blog
Islands architecture, zero JavaScript by default, type-safe Content Collections: what won us over after evaluating Hugo, Eleventy and Next.js.
When we decided to start this blog, the question wasn’t whether to use a static site generator — we were aligned on that — but which one. For two weeks we benched four candidates side by side: Hugo, Eleventy, Next.js (static export mode) and Astro. In the end we picked Astro. Here’s why.
The criteria that actually mattered
We weren’t looking for the “best framework” in the abstract — we were looking for the one that minimized friction for our specific team (TypeScript/JavaScript devs with some Ruby background) and met three non-negotiable requirements: GitHub Pages deploy, IT/EN multilingual, and internal search with no backend.
Beyond those we had softer but real constraints: a modern custom design system, flawless performance (it is a software house’s calling card), and a developer experience that wouldn’t slow down publishing.
Why not Hugo
Hugo is fast. Tremendously fast. It compiles 10,000 pages in under 10 seconds, vs. 30-90 for Astro. But for a company blog, even with an optimistic hundred articles, we’re talking about one or two seconds of build time either way: the theoretical advantage evaporates.
What’s left is Go templating. A JS/TS team can learn {{ }} syntax in an afternoon, but every time you want to extend a layout or build a slightly more complex component you pay friction. And existing themes often carry assumptions that lock you in.
Why not Eleventy
We liked Eleventy. It’s elegant, JavaScript-first, configurable to the bone. But it’s also a “do it yourself” framework: to match Astro on type safety (Zod schemas on frontmatter), built-in i18n routing and image optimization, we’d have to assemble the pieces ourselves through community plugins or custom code.
For a single developer who wants maximum control, it’s the right call. For a team that has to maintain it over time, every added plugin is a potential debt surface.
Why not Next.js
Next.js would have been overkill: we’d be importing the whole hybrid rendering model (SSR, ISR, client components) to generate static HTML. The final bundle, even after export, carries runtime we don’t need. And configuring static i18n in Next with the new App Router is still a fragile exercise.
What Astro won on
Seven things, in order of importance.
Content Collections. Defining a Zod schema for our article frontmatter gives us editor autocomplete, build-time validation, and zero chance of publishing a post tagged frontned instead of frontend. For anyone who’s suffered through ten years of typos in repos, this alone justifies adoption.
const blog = defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/data/blog" }),
schema: ({ image }) => z.object({
title: z.string().max(120),
pubDate: z.coerce.date(),
tags: z.array(z.string()).default([]),
cover: image().optional(),
}),
});
The corresponding types are auto-generated in .astro/types.d.ts on every dev/build: getCollection('blog') hands you back typed objects without writing a single .d.ts by hand, with no drift between schema and consumer code.
Native i18n. Since 4.0 Astro has an i18n API in core: you configure locales, defaultLocale, a fallback strategy, and you get helpers like getRelativeLocaleUrl() and Astro.currentLocale. No third-party plugins, no magic routing conventions.
The islands. Islands architecture is the most interesting technical differentiator: the page is static HTML, and interactive components (language switcher, theme toggle, live search) are “islands” that hydrate independently. On this very page, the JS we ship to the browser is under 5 KB — most of it is the language switcher.
Two details that matter in practice:
- Islands are framework-agnostic: in the same page you can mix React, Vue, Svelte, Solid, Preact. For a multi-stack team it’s the guarantee of not having to pick a language up front — when we’ll have a particularly complex island, we’ll use the right tool for that piece, not the one the framework forces on us.
- You get granular control over when each island hydrates:
client:load(right away),client:visible(when it enters the viewport),client:idle(when the main thread is free),client:media="(max-width: 768px)"(only at that media query). The theme toggle in our Header isclient:loadbecause it has to be live before first paint; a hypothetical form at the bottom of the page would beclient:visible.
The .astro format. A .astro file is the closest thing to “server-only JSX” we’ve found. A TypeScript block on top for fetching and rendering logic, an HTML template below, and optionally CSS at the bottom that is automatically scoped to the component. No JavaScript shipped to the browser unless we explicitly opt in. Simplified example:
---
import { getLangFromUrl } from "@/i18n/utils";
const lang = getLangFromUrl(Astro.url);
---
<nav>
<a href={`/${lang}/blog`}>Blog</a>
</nav>
<style>
nav { display: flex; gap: 1rem; }
</style>
The <style> is automatically scoped: no paranoid class names, no CSS Modules, no styled-components. For a team that wants CSS control without the trap of global cascade, this is exactly the right balance.
MDX, when Markdown isn’t enough. Most articles are plain .md: prose, code blocks, links. When something more is needed — a custom callout, an interactive chart, a side-by-side comparison of two snippets — you rename the file to .mdx and you can import Astro components (or React/Vue/Svelte if you need them as islands) and use them inline with the rest of the prose:
---
title: "Field protocols in IoT"
pubDate: 2026-04-12
---
import Callout from "@/components/Callout.astro";
import LatencyChart from "@/components/charts/LatencyChart.astro";
Industrial IoT has three dominant field protocols — Modbus, OPC UA, MQTT.
<Callout type="warning">
Modbus has no authentication: always wrap it in TLS or a VPN.
</Callout>
<LatencyChart data={[{p: "Modbus", ms: 12}, {p: "OPC UA", ms: 8}]} />
Three concrete advantages over plain Markdown:
- Reusable components instead of copy-pasting HTML blocks. The callout above is one
.astrofile you write once. - Same types as the rest of the project: the imported component has typed props, so a misuse is caught by the editor at write time, not by a reader at runtime.
- The Zod frontmatter schema stays identical and auto rendering too: no parallel syntax like Hugo’s shortcodes or Eleventy’s includes. For articles that stay pure Markdown there’s no cost: you pay for MDX only where you use it.
The only caveat is not to overdo it. If every article requires ten custom imports, the value of the prose slides behind the boilerplate: MDX is a tool for the cases where Markdown isn’t enough, not a replacement for it.
Images as first-class citizens. <Image /> and getImage() automatically resize, convert to WebP/AVIF and generate srcset for responsive delivery. More importantly: the Zod schema accepts image() as a field type (see cover in the snippet above), so when you declare a cover in an article’s frontmatter, Astro imports it, validates it, optimises it and hands you back a fully processed object at build time. Hugo needs plugins to get close, Eleventy makes you write shortcodes, Next requires next/image with hosting constraints that lock you to Vercel if you want on-the-fly optimisation.
View Transitions, without becoming a SPA. Since Astro 4 there’s <ClientRouter />, a component that uses the browser’s native View Transitions API to do client-side navigation between static pages. The result: the feel of a SPA (no white flash on click) without the cost of a SPA — no huge JS bundle, no loss of shared state, no SEO to patch up. We’re not using it on this blog yet, but it’s a free upgrade path the other candidates don’t offer.
“Astro feels more like a complete web framework, offering features like file-based routing, content collections with type safety, and built-in image optimization.”
The trade-offs we accepted
No choice is free. What we gave up by going Astro instead of the alternatives:
- Build time vs. Hugo, if one day we have thousands of articles. Not a problem today.
- Plugin ecosystem vs. Next.js. Astro is growing fast but doesn’t have the same critical mass.
- Familiarity for anyone coming from WordPress or Wix. But none of the candidates helped here.
Next steps
In upcoming posts we’ll cover Pagefind integration for client-side search, how we modelled i18n with a translationKey to link equivalent articles between languages, and the GitHub Pages deploy workflow. Stay tuned.