Astro — Modern Static Site Builder with Islands Architecture
In this tutorial, you will learn about Astro. We cover key concepts, practical examples, and best practices to help you master this topic.
Astro is a modern static site Builder that ships zero JavaScript by default and uses islands architecture for selective client-side hydration.
What You'll Learn
By the end of this tutorial, you'll understand Astro's islands architecture, how to build components with Astro's template syntax, integrate framework components, and optimize static output.
Why It Matters
Astro solves the JavaScript bloat problem by shipping zero client-side JS by default. Only interactive components (islands) include JavaScript, making Astro sites incredibly fast while still supporting React, Vue, or Svelte components.
Real-World Use
A marketing site uses Astro with React interactive components. The hero section, testimonials carousel, and pricing calculator are React islands. The rest is pure HTML. Page load is under 1 second with 0KB JavaScript until interaction.
Astro Architecture
graph TD
A[Astro Build] --> B[.astro components]
A --> C[.md / .mdx content]
A --> D[Framework components]
D --> E[React .jsx]
D --> F[Vue .vue]
D --> G[Svelte .svelte]
E --> H[Build time rendering]
F --> H
G --> H
B --> H
C --> H
H --> I[Static HTML output]
H --> J[Island JavaScript]
I --> K[CDN Deploy]
J --> K
J --> L[Client hydration
only interactive parts]
style H fill:#e67e22,color:#fff
style J fill:#4a90d9,color:#fff
style L fill:#27ae60,color:#fff
Astro Component
---
// src/pages/index.astro
// Frontmatter: runs at build time only
import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro';
import Counter from '../components/Counter.jsx'; // React island
const posts = await fetch('https://api.example.com/posts')
.then(r => r.json());
const siteTitle = 'My Astro Site';
---
<Layout title={siteTitle}>
<main>
<h1>{siteTitle}</h1>
<p>This page has zero JavaScript by default.</p>
<section class="posts">
{posts.slice(0, 5).map(post => (
<Card title={post.title} description={post.body.substring(0, 100)} />
))}
</section>
{/*
Interactive React component — only this loads JS.
client:load = hydrate immediately on page load
*/}
<Counter client:load />
</main>
</Layout>
Islands Architecture
---
// src/components/Counter.jsx — React island
import { useState } from 'react';
export default function Counter({ start = 0 }) {
const [count, setCount] = useState(start);
return (
<div class="counter-island">
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
<button onClick={() => setCount(c => c - 1)}>-</button>
</div>
);
}
---
<!-- Client directives control hydration:
client:load — Hydrate immediately
client:idle — Hydrate when browser is idle
client:visible — Hydrate when element is visible
client:media — Hydrate at specific breakpoint
client:only — Render only on client (no SSR)
-->
<Counter client:idle />
<Counter client:visible start={100} />
<Counter client:only="react" />
Content Collections
---
// src/pages/blog/[...slug].astro
import { getCollection } from 'astro:content';
// Get all blog posts
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>{post.data.date.toLocaleDateString()}</time>
<div class="content">
<Content />
</div>
</article>
</Layout>
# src/content/config.ts — Content collection schema
import { defineCollection, z } from 'astro:content';
const blogCollection = defineCollection({
schema: z.object({
title: z.string(),
description: z.string(),
date: z.date(),
tags: z.array(z.string()).optional(),
draft: z.boolean().default(false),
image: z.string().optional(),
}),
});
export const collections = {
blog: blogCollection,
};
---
# src/content/blog/astro-guide.md
title: "Building with Astro"
description: "A guide to Astro's islands architecture and static site generation"
date: 2026-06-28
tags: ["astro", "ssg"]
draft: false
---
## Getting Started
Astro is a web framework for building content-driven websites.
## Islands Architecture
The key insight: most page content doesn't need JavaScript.
Framework Integration
---
// src/pages/index.astro
import ReactCard from '../components/ReactCard.jsx';
import VueCard from '../components/VueCard.vue';
import SvelteCard from '../components/SvelteCard.svelte';
---
<html>
<head>
<title>Multi-Framework Astro</title>
</head>
<body>
<h1>Astro with Multiple Frameworks</h1>
<div class="cards">
<ReactCard title="React Card" client:visible />
<VueCard title="Vue Card" client:visible />
<SvelteCard title="Svelte Card" client:visible />
</div>
<p class="static-note">
This paragraph has zero JavaScript. The cards above
load JS only when scrolled into view.
</p>
</body>
</html>
// astro.config.mjs — Framework integrations
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import vue from '@astrojs/vue';
import svelte from '@astrojs/svelte';
export default defineConfig({
integrations: [
react(),
vue(),
svelte()
],
output: 'static'
});
Common Mistakes
- Using client:load on every component. client:load hydrates immediately, defeating Astro's performance advantage. Use client:visible or client:idle for below-the-fold components.
- Forgetting to configure output: 'static'. Default Astro is static. But if you add SSR features, set output: 'static' explicitly to maintain SSG behavior.
- Mixing Astro and JSX syntax. Astro files use a different syntax than React. Template expressions use { } but event handlers and hooks only work in framework components.
- Not using content collections for validation. Raw markdown files lack schema validation. Content collections provide TypeScript validation for frontmatter.
- Over-using framework components. Not everything needs to be React/Vue. Use Astro components for static content and only hydrate what truly needs interactivity.
Practice Questions
- What is Astro's islands architecture and how does it reduce JavaScript?
- What client directives control when islands hydrate?
- How do Astro content collections validate frontmatter?
- Can you use multiple JavaScript frameworks in one Astro project?
- How does Astro handle static generation vs SSR?
Challenge: Build an Astro marketing site with a static hero section, a React interactive testimonial carousel (client:visible), a Vue pricing calculator (client:idle), and content collections for blog posts.
FAQ
Mini Project
Build an Astro portfolio site with: a static hero section using Astro components, a React project gallery (client:visible), a Vue contact form (client:idle), markdown blog content with collections, and zero JavaScript above the fold.
What's Next
You've built an Astro site. Now learn how to manage Markdown Content effectively across different SSG frameworks.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro