Why Astro is the fastest framework for landing pages

This site — the one you’re looking at right now — runs on Astro 6 and scores 98/100 on Performance in PageSpeed Insights. We don’t use an image CDN, we don’t use SSR, we don’t use service workers.

This is what we learned building it and why we recommend it for product landing pages.

The problem with modern frameworks

React, Vue, Svelte — they’re all excellent for applications. But when 90% of your landing page is static content, shipping a 100KB+ JavaScript runtime to the browser is overhead with no value.

Most landing pages need:

  • Text and images (pure HTML + CSS)
  • A form (maybe 5KB of JS)
  • A scroll animation (CSS transforms)
  • Analytics (deferred script)

Next.js ships React + ReactDOM + your page bundle. Astro ships only what you need.

Zero JS by default

Astro works on a simple principle: if it’s not interactive, don’t ship JavaScript.

A pure Astro component renders to static HTML. Period. No hydration, no virtual DOM, no reconciliation.

---
// This component compiles to HTML + CSS. Zero JS.
const stats = [
  { label: 'Performance', value: '98/100' },
  { label: 'SEO', value: '100/100' },
];
---
<div class="grid grid-cols-2 gap-4">
  {stats.map(stat => (
    <div class="p-4 border rounded">
      <p class="text-2xl font-bold">{stat.value}</p>
      <p class="text-sm text-gray-500">{stat.label}</p>
    </div>
  ))}
</div>

If you need interaction, you use Islands Architecture:

<!-- Only this component ships JS to the browser -->
<InteractiveForm client:visible />

client:visible means: “load the JavaScript only when the user scrolls here.” The rest of the page is static.

Real comparison: Astro vs Next.js

Same landing page, same content, same CSS:

MetricAstro (this site)Next.js 14 (App Router)
First Contentful Paint0.9s1.4s
Largest Contentful Paint1.8s2.6s
Total Blocking Time0 ms120 ms
JS transferred2.1 KB128 KB
Time to Interactive1.2s2.9s
Lighthouse Performance9882

Next.js is superior for state-heavy applications. Astro wins for content-first sites by design.

Optimizations we apply

1. Self-hosted fonts

We don’t load Google Fonts from their CDN. We use @fontsource-variable with latin-only subset:

  • Space Grotesk Variable: headlines, 42KB
  • Inter Variable: body text, 65KB

Direct preload in <head>:

<link rel="preload" href="/fonts/space-grotesk-latin-wght-normal.woff2" as="font" type="font/woff2" crossorigin />

With font-display: optional, the browser uses the font if it’s already cached, or falls back immediately. Zero layout shift.

2. CSS merged into a single chunk

In astro.config.mjs:

build: {
  cssCodeSplit: false,
}

This generates a single CSS file instead of chunks per route. For a single-page landing, one fewer HTTP request = better.

3. Minified HTML

build: {
  compressHTML: true,
}

Removes whitespace, comments, and redundant attributes. Saves ~15% in transfer size.

4. GPU-composited animations

All animations use transform and opacity:

.scroll-reveal {
  transform: translateY(20px);
  opacity: 0;
  transition: transform 0.6s ease, opacity 0.6s ease;
}
.scroll-reveal.visible {
  transform: translateY(0);
  opacity: 1;
}

We don’t animate width, height, top, left. That forces layout recalculation (reflow) and jank on mobile.

5. Deferred analytics

Plausible Analytics loads with requestIdleCallback post-window.load:

window.addEventListener('load', function () {
  'requestIdleCallback' in window
    ? requestIdleCallback(loadPlausible)
    : setTimeout(loadPlausible, 0);
});

It doesn’t even compete with parsing the critical HTML.

When NOT to use Astro

Astro isn’t the answer to everything. Don’t use it if:

  • You need complex global state (use Next.js + Zustand)
  • Your app is primarily an interactive dashboard (pure React or Remix)
  • You need SSR with per-request revalidation (Next.js ISR)
  • Your team only knows React and doesn’t want to learn another framework

Content Collections: blogs and docs with no effort

The feature that convinced us most about Astro is Content Collections:

// src/content.config.ts
import { defineCollection, z } from 'astro:content';

const posts = defineCollection({
  schema: z.object({
    title: z.string(),
    date: z.coerce.date(),
    tags: z.array(z.string()),
  }),
});

Markdown with Zod-validated frontmatter, typed safety, and automatic SEO per post.

This blog you’re reading runs on Content Collections. Zero boilerplate for listings, categories, or RSS.

Conclusion

For landing pages, portfolios, blogs, and documentation, Astro is the framework with the best performance / complexity ratio in 2025.

It’s not that Next.js is slow. It’s that Next.js brings a runtime you don’t need. Astro takes the decision off your hands: it only ships JS when you explicitly ask for it.

Want a landing that loads in under 1 second? Let’s talk.