Most modern frameworks ship JavaScript to the browser whether you need it or not. A blog post rendered by Next.js or Nuxt still downloads and executes a JavaScript runtime to hydrate static content that never changes. Astro takes the opposite approach: it renders every page to static HTML at build time and ships zero client-side JavaScript by default. Interactive components are hydrated individually using the Islands Architecture. Only the interactive parts load JS. Everything else is pure HTML and CSS.
When Astro Makes Sense
Astro is purpose-built for content-driven websites: blogs, documentation, marketing pages, portfolios, landing pages, and e-commerce storefronts. If your site is primarily content with islands of interactivity, Astro will outperform a React SPA on every performance metric. If you are building a highly interactive dashboard or real-time application, stick with a full SPA framework.
Project Setup
# Create a new Astro project
npm create astro@latest my-blog
cd my-blog
# Add integrations as needed
npx astro add react # Use React components
npx astro add tailwind # Add Tailwind CSS
npx astro add mdx # Write content in MDX
Astro is framework-agnostic. You can use React, Vue, Svelte, Preact, Solid, or Lit components within the same project. Mix and match based on what your team knows.
Astro Components
Astro has its own .astro component format. The frontmatter (between the --- fences) runs at build time. The template below renders to static HTML:
---
// src/pages/blog/[slug].astro
import Layout from '../../layouts/Layout.astro';
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<Layout title={post.data.title}>
<article>
<h1>{post.data.title}</h1>
<time datetime={post.data.date.toISOString()}>
{post.data.date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}
</time>
<Content />
</article>
</Layout>
This page ships zero JavaScript. The frontmatter fetches content, the template renders HTML, and the output is a static .html file.
Islands Architecture: Selective Hydration
When you need interactivity, wrap the component with a client:* directive:
---
import SearchBar from '../components/SearchBar.jsx';
import Newsletter from '../components/Newsletter.svelte';
import TableOfContents from '../components/TableOfContents.astro';
---
<!-- Static: renders to HTML, ships no JS -->
<TableOfContents headings={headings} />
<!-- Hydrates immediately on page load -->
<SearchBar client:load />
<!-- Hydrates only when scrolled into viewport -->
<Newsletter client:visible />
<!-- Hydrates only when the browser is idle -->
<RecommendedPosts client:idle />
<!-- Hydrates only on wide screens -->
<DesktopSidebar client:media="(min-width: 768px)" />
The directives give you precise control over when JavaScript loads. client:visible is particularly powerful for below-the-fold components: they render as static HTML for fast LCP, and hydrate only when the user scrolls to them.
Content Collections
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
date: z.date(),
description: z.string().max(200),
tags: z.array(z.string()),
draft: z.boolean().default(false),
image: z.string().optional(),
}),
});
export const collections = { blog };
If a blog post’s frontmatter is missing a required field or has the wrong type, you get a build-time error. This catches content issues before they reach production.
Performance Results
On a typical blog with 50 posts, Astro produces pages that score 100/100 on Lighthouse Performance. Time to First Byte under 50ms from a CDN. Largest Contentful Paint under 1 second. Total blocking time: 0ms. Compare this to a Next.js blog where even a static page downloads 80-200KB of JavaScript for the React runtime. For content sites where SEO and Core Web Vitals directly impact discoverability, the difference matters.
Astro is not trying to replace React or Next.js for application development. It is purpose-built for a specific, massive category of the web: sites where content is king and performance is non-negotiable. If that describes your project, Astro is the strongest choice available.
Further reading: Astro Documentation | Islands Architecture | Astro Theme Gallery

Leave a Reply