Why I Chose Astro Over Next.js, SvelteKit and Hugo
astro, next.js, sveltekit, hugo, wordpress, architecture-decision
The problem
Astro ships zero JavaScript by default, treats content as a first-class citizen, and lets you use any UI framework per component. That’s why I picked it over Next.js, SvelteKit, Hugo and WordPress for this portfolio. The result: Lighthouse 100, sub-5-second builds, free hosting on Cloudflare Pages. Here’s the full breakdown.
It was a Tuesday evening in my flat in London. I had my old portfolio open in one tab, the one I’d built two years ago with a framework that felt modern at the time. The design had aged badly. The Lighthouse score sat somewhere in the low 70s. The build took 45 seconds and I couldn’t remember why half the dependencies existed. I closed the tab, opened a blank editor, and stared at the cursor for a good ten minutes. Not writing. Just thinking about what I actually needed this time. Not what was trendy. What was right.
That evening turned into a framework evaluation session. Five tabs open, each one a different docs site. Coffee going cold on the desk. I kept a plain text file with two columns: what each tool gives me and what it costs me. By midnight the list had narrowed to two. By Wednesday morning, one.
The requirements were simple:
- Fast. Actually fast. Static HTML, no spinners, no layout shifts.
- Content-driven. I want to write in Markdown, not fight a CMS.
- Low maintenance. No database, no server, no monthly bill for a blog.
- MDX support so I can drop interactive components into articles when it makes sense.
- Full control over the design. No themes, no templates, no constraints.
With that list in front of me, I evaluated five options seriously.
Why not WordPress?
WordPress runs 43% of the web. It has plugins for everything. Themes for everything. That’s precisely the problem.
WordPress is built for people who don’t want to write code. I do. I want to write my own CSS, control my own markup, decide exactly what gets shipped to the browser. WordPress fights you on all three.
The performance story is painful. A default WordPress install ships jQuery, multiple render-blocking stylesheets and a cascade of plugins that each add their own JavaScript. You can optimise it (caching layers, CDN, minification plugins) but you’re optimising around bad defaults instead of starting from good ones.
The hosting model is also wrong here. WordPress needs PHP, a database and a server. For a blog. That’s a recurring cost, a security surface and operational overhead I don’t want.
WordPress solves a real problem for a lot of people. Just not for someone who builds distributed systems for a living and wants a static site. Does that make WordPress bad? No. Does it make it wrong for me? Completely.
Why not Next.js?
I use Next.js every day at work. App Router, Server Components, server actions. I know the whole stack. Picking it would have been the comfortable choice.
Comfort is not a good reason to choose a tool.
Next.js is designed for web applications. Dynamic routes, server-side rendering, API endpoints, authentication flows, database connections. It’s exceptional at that. For a personal portfolio with static content, it’s a lorry carrying a backpack.
The JavaScript cost is the real issue. Next.js ships a React runtime to every page, even if the page has zero interactivity. The hydration step exists whether you need it or not. For a blog post that’s pure text, that’s wasted bytes and wasted time.
Server Components help, but they solve a server-rendering problem. I don’t have a server. I have Markdown files and a build step.
I also wanted to avoid React lock-in for this project. If I want to drop in a Svelte component or a vanilla Web Component in two years, I should be able to without rewriting the site.
Why not SvelteKit?
SvelteKit was the hardest to say no to. Svelte compiles components to vanilla JavaScript with no runtime. The syntax is clean, the performance is excellent, SvelteKit handles both static generation and server rendering.
For a content site, SvelteKit can prerender every page at build time. The result is fast. But “fast” is not “zero.” Every page still ships Svelte’s hydration code, even when there’s nothing to hydrate. Small cost. But you pay it on every single page load. And the people reading your site pay it too, whether they asked for interactivity or not.
The bigger issue: SvelteKit is a full application framework. Routing, form actions, hooks, load functions. For a blog, I’d use maybe 10% of what it offers. The rest is dead weight in my mental model, not in the bundle.
SvelteKit also means committing to Svelte. If I want a React component in an article, I need adapters, wrappers or workarounds. Astro lets me use any framework per component. That flexibility won.
If I were building a small web app with some content pages, SvelteKit would be my first choice. For a content site with occasional interactivity, Astro fits better.
Why not Hugo?
Hugo is the fastest static site generator. Period. Build times measured in milliseconds, not seconds. Zero JavaScript by default. A single Go binary with no dependencies.
For pure Markdown blogs, Hugo is nearly perfect. The problem is everything else.
Hugo’s templating language is Go’s html/template. If you’ve never written Go templates, good luck. Partials, shortcodes and layouts work, but the syntax feels like it was designed for machines, not humans.
The real dealbreaker: no component model. If I want to embed an interactive chart in a blog post, Hugo has no answer. Shortcodes can inject HTML but they can’t mount a React or Svelte component with state, props and lifecycle. MDX doesn’t exist in Hugo’s world.
Hugo is the right tool if your content is pure text and you never need interactivity. The moment you want a single interactive element in an article, you hit a wall that doesn’t move.
What makes Astro different?
Astro starts from a different premise. Ship zero JavaScript by default. If a page is static content, the browser gets pure HTML and CSS. Nothing else.
That single decision changes everything downstream. It’s like the difference between a building designed around natural light and one where you retrofit windows after the walls are up. The architecture has to start from the right premise.
Built for this exact use case.
| Framework | JS by default | Content-first | Component model | Best for |
|---|---|---|---|---|
| Astro | Zero | Yes (Content Collections) | Any framework | Content sites, portfolios, blogs |
| Next.js | React runtime | No | React only | Web apps, dashboards |
| SvelteKit | Svelte hydration | No | Svelte only | Small apps with content |
| Hugo | Zero | Yes (Markdown) | None | Pure text blogs |
| WordPress | jQuery + plugins | CMS-driven | PHP templates | Non-technical teams |
Content Collections
Astro treats content as a first-class concept. You define a schema with Zod, put your Markdown files in a directory, get type-safe access to your frontmatter at build time.
const blog = defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/blog" }),
schema: z.object({
title: z.string(),
description: z.string(),
date: z.coerce.date(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
});No runtime surprises. No “this field is undefined in production because someone forgot a frontmatter key.” The build fails if the data is wrong. That’s the kind of safety net I expect from a typed system.
Islands architecture
When I do need interactivity (like the comparison chart above) Astro loads the component in isolation. The rest of the page stays as static HTML.
This is the right mental model. Interactivity is opt-in, not opt-out. Most of this site is text. The few interactive pieces get their own JavaScript budget without taxing the rest.
Framework-agnostic
That comparison chart is written in React because I know React. Astro doesn’t care. I could rewrite it in Svelte, Solid, Vue or vanilla JS tomorrow. The rest of the site wouldn’t change.
For a personal project I plan to maintain for years, that flexibility matters. I’m not betting my portfolio on the React ecosystem staying the best choice forever.
What are the gotchas with Astro in 2026?
Smoother than expected, but a few things caught me off guard.
Tailwind CSS v4 uses a different integration path. The old @astrojs/tailwind package is deprecated. You use the Vite plugin directly, which is actually simpler. One less abstraction layer.
The rehype-pretty-code setup was the one that nearly broke me. I had shiki installed, the config looked right, the build passed. But the syntax highlighting was rendering as plain monospace text with no colours. I spent close to an hour comparing my config against the docs, swapping options, restarting the dev server. Turned out Astro’s built-in syntax highlighting was silently overriding the rehype plugin. You have to explicitly disable it in the Astro config. One line. Sixty minutes of my evening to find it. The kind of thing that makes you question every abstraction layer between you and the browser.
Content Collections in Astro 6 use the Content Layer API with explicit loaders. The file-based convention from v4 is gone. Better architecture, but the migration docs could be clearer. I’m still not sure I’ve set mine up optimally.
Cloudflare Pages deployment works out of the box with @astrojs/cloudflare. No configuration beyond the adapter. Build times under 10 seconds.
The result
This site ships zero JavaScript on every page that doesn’t need it. The blog post you’re reading right now is pure HTML and CSS. The only JavaScript on this page is the interactive chart above, loaded on demand.
Build time: under 5 seconds. Hosting cost: free on Cloudflare Pages. And the Lighthouse score. I ran it the first time expecting maybe a 95, some image optimisation left to do, a render-blocking CSS file somewhere. All four categories came back 100. I sat there looking at those green circles for a moment, genuinely surprised. Not because 100 is impossible. Because I’d never shipped something that started there without needing to chase performance after the fact. The architecture did the work. I just didn’t get in its way.
For a content site, that’s what good defaults look like. You don’t optimise your way to performance. You start there and add complexity only when the content demands it. The design decisions behind this portfolio pushed that philosophy further than I expected.
When I wouldn’t choose Astro
Astro is not the answer to everything. If I were building a dashboard with real-time data, I’d reach for Next.js or SvelteKit. A collaborative app with auth and state, same. A marketing site for a non-technical team… maybe WordPress, or a headless CMS with Astro if the team has a developer nearby.
The right tool depends on the problem. For a portfolio and blog built by someone who writes code, Astro is the right tool. I built the entire site with Claude Code, which made the framework choice matter even more. Astro’s simple file-based structure made AI-assisted development remarkably smooth.