Skip to content
Back to blog
7 min read

Fast Websites with Astro: Islands, Zero JS and Core Web Vitals

How I build fast websites with Astro: islands architecture, zero JS by default, CSS purging and Core Web Vitals (LCP/INP) to improve SEO and conversion.

AstroPerformanceCore Web VitalsSEOFrontend

Most websites aren’t slow because the server is slow. They’re slow because the user’s browser has to download, parse and execute megabytes of JavaScript before it can do anything useful. You ship a full SPA framework to paint a landing page that is, at its core, text and images. You pay the cost of complete hydration just to get three interactive buttons. That’s the problem Astro solves at the root, and it’s the reason I built this very portfolio with it.

In this article I’ll walk through how to build fast websites with Astro without tricks: the islands architecture, the zero-JS-by-default principle, CSS purging, and how all of that lands on real Core Web Vitals numbers. Spoiler: it isn’t magic, it’s deciding what runs on the client and what doesn’t.

The real cost of JavaScript

Before talking about Astro, it helps to understand what you pay for with a traditional SPA approach. When you serve a React or Next.js app in client mode, the flow is:

  1. The browser downloads the HTML (often nearly empty).
  2. It downloads the JavaScript bundle.
  3. It parses and executes it.
  4. React rebuilds the DOM and rehydrates every component to reconnect the event handlers.
  5. Only then does the page respond to the user.

Steps 2 through 5 are time when the user sees something on screen but can’t interact with it. On a mid-range phone – which is the real device behind a good chunk of your traffic – that hydration is noticeable. And here’s the nuance almost nobody mentions: most of that JavaScript rebuilds an interface that was already there in the HTML. It’s duplicated work.

Zero JS by default: the design decision that changes everything

Astro flips the question. Instead of “how do I hydrate the whole app?”, it asks “which parts actually need JavaScript?”. By default, an Astro component renders to static HTML and ships zero bytes of JS to the client. Your layout, your sections, your content: HTML and CSS, the way it should be.

This isn’t a limitation, it’s the correct default. An “About” section doesn’t need a 40 KB runtime to show a paragraph. When you do need interactivity – a form, a terminal, a theme toggle – you add it explicitly and in isolation. That’s what’s called islands architecture.

Islands architecture: hydrate only what breathes

An island is an interactive component (React, Preact, Svelte, Vue…) embedded in a sea of static HTML. Each island hydrates independently and on demand, without dragging the rest of the page along. Astro gives you fine-grained control over when that hydration happens through directives:

<!-- Hydrates as soon as the browser is idle -->
<ThemeToggle client:idle />

<!-- Hydrates only when it enters the viewport -->
<AiTerminal client:visible />

<!-- Never hydrates: pure HTML -->
<About />

The difference between client:idle and client:visible looks minor, but it’s exactly the lever that decides your performance. On this portfolio I apply a cost-based strategy:

  • Heavy islands below the fold (the AI terminal, the contact form) use client:visible. Their JavaScript isn’t downloaded until the user scrolls and actually sees them. If they never reach it, they never pay for it.
  • Lightweight islands (the theme toggle, the back-to-top button) use client:idle. They load when the main thread is free, without competing with the initial render.

There are only four islands in the entire site. The rest – 90% of the DOM – is HTML the browser paints without executing anything. That’s the foundation of a fast website: it’s not that the JavaScript is efficient, it’s that most of it doesn’t even exist on the client.

Preact instead of React when weight matters

React 19 is excellent, but its runtime is heavy. When an island is trivial – a couple of state values, no ecosystem libraries – Astro lets you switch to Preact, an alternative with the same API at a fraction of the size (around 3 KB versus React’s tens of KB). For a theme toggle or a counter, the bundle difference is dramatic and the user notices no functional change at all. The practical rule: use React when you need its ecosystem; use Preact when you only need lightweight reactivity.

CSS: ship only what you use

The other big invisible drag is CSS. Utility frameworks like Tailwind can generate thousands of classes, but your page uses a tiny fraction of them. The key is purging: in production, the compiler scans your markup and removes every class that doesn’t appear, leaving a tiny stylesheet.

On this portfolio I use Tailwind CSS 4 + DaisyUI 5 defined CSS-first, and Astro additionally inlines the critical CSS and splits the rest per route. The result is that each page loads only the styles it needs, with no single monolithic bundle.css you drag everywhere. Less CSS to download and parse translates directly into a faster first paint.

Core Web Vitals: where all of this gets billed

The decisions above aren’t engineer aesthetics: they’re measured. Google evaluates the experience with Core Web Vitals, and each one maps to something concrete that Astro attacks:

  • LCP (Largest Contentful Paint) – how long the main element takes to appear. With HTML served directly and no waiting on hydration, the content paints sooner. Combined with optimized WebP images and loading="lazy" for everything below the fold, LCP drops with no effort.
  • INP (Interaction to Next Paint) – how long the page takes to respond to a click or tap. Since the main thread isn’t saturated hydrating the whole app, it stays free to respond. Less JavaScript running = snappier interactions.
  • CLS (Cumulative Layout Shift) – how much the layout “jumps”. You control it by reserving dimensions for images and fonts. I load Geist Variable and Geist Mono self-hosted to avoid the font flash and the associated reflow.

One detail I take particular care with: the no-flash theme. Light/dark mode is set by a tiny inline script in the <head>, before the first paint. That way the user never sees a flash of the wrong theme, a subtle but annoying CLS failure that many sites with a theme switcher carry.

A concrete technique: heavy computation off the main thread

When an interaction does require expensive work, the answer isn’t “fewer features” but moving that computation somewhere it won’t block the UI. In RecruitSecure AI I compute vector embeddings of resumes inside the browser, and to keep the interface from freezing that calculation runs in dedicated Web Workers. The main thread stays free, INP doesn’t suffer, and the page keeps responding while it processes in the background. It’s the same Astro philosophy – don’t block the main thread – applied to application logic. This is also how practical on-device AI stays usable: heavy inference belongs off the UI thread.

Why this matters for SEO and conversion

Here’s the part that interests whoever pays for the site, not just whoever codes it. Core Web Vitals are a ranking signal in Google: between two pages with equivalent content, the faster one ranks better. But the most direct effect is on conversion. Every tenth of a second of waiting erodes user intent; a page that responds instantly retains, one that drags loses visitors before it even loads.

A fast website with Astro isn’t a technical indulgence: it’s less bounce, better rankings and more conversions for the same investment in content and design. Performance is a business feature disguised as an engineering detail.

When Astro is the right choice (and when it isn’t)

Honestly, Astro isn’t the tool for everything. It shines at what’s predominantly content with islands of interactivity: portfolios, blogs, landing pages, documentation, e-commerce, marketing sites. For an application with dense state and many interactive views – a real-time dashboard, an editor – the SPA model with React or Next still makes sense, and that’s in fact how I built DevFlow AI and the monitoring dashboard. The skill isn’t marrying a framework, it’s choosing the right rendering model for each problem. Astro and Next don’t compete: they solve different things.

What I take away

Building fast websites with Astro isn’t about optimizing at the end, it’s about deciding from the start what runs on the client. Zero JS by default, islands hydrated with intent, purged CSS, and a constant eye on LCP and INP. The result is a website that loads fast on your user’s real phone, ranks better and converts more, without sacrificing interactivity where it genuinely matters.

If you have a project where load speed is costing you traffic or conversions – or you want a site that passes Core Web Vitals without patches – let’s talk. It’s exactly the kind of problem I enjoy digging into.