Table of Contents
1399 words
7 minutes
Getting Started with Astro: A Modern Content-First Web Framework

1. Introduction#

Astro is a web framework designed specifically for content-driven websites. Its core philosophy is simple: if most of a page’s content doesn’t need JavaScript, then JavaScript shouldn’t be sent.

While the React/Vue community races on server-side rendering, streaming, and everything else, Astro took a completely different path:

Your blog posts, documentation pages, marketing sites—99% of the content is static. Why load a full JS runtime for that 99%?

This approach made Astro an instant hit—it won first place in “Frontend Satisfaction” for two consecutive years (2023/2024), and in 2025 became one of the fastest-growing web frameworks on GitHub by stars.

(The blog you’re reading right now is built with Astro.)


2. Islands Architecture: Astro’s Killer Feature#

2.1 The Problem with Traditional SPAs#

Traditional React/Vue single-page applications:

The entire page = one giant JS application
├── Navigation bar (requires interactivity)
├── Article content (purely static text)
├── Sidebar (purely static)
├── Comment section (requires interactivity)
└── Footer (purely static)

To load this page, the browser has to download the entire application’s JS, then “hydrate” all components. Even if 80% of the content never changes, you still pay the JS cost for it.

2.2 Astro’s Answer#

Whole page = pure HTML (default)
├── Navbar → pure HTML (zero JS)
├── Article content → pure HTML (zero JS)
├── Sidebar → pure HTML (zero JS)
├── 🏝️ Comment section → React component (hydrated on demand)
└── 🏝️ Like button → Vue component (hydrated on demand)

This is islands architecture: pages are static HTML by default, and only the parts that truly need interactivity load JS as “islands.” Each island loads and hydrates independently, without interfering with the others.


3. Creating Your First Astro Project#

3.1 Initialization#

Terminal window
# npm
npm create astro@latest my-site
# pnpm (recommended)
pnpm create astro@latest my-site

The initialization wizard:

✔ How would you like to start? → Empty
✔ Do you plan to write TypeScript? → Yes
✔ How strict should TypeScript be? → Strict
✔ Install dependencies? → Yes
✔ Initialize a new git repository? → Yes

Start the dev server:

Terminal window
cd my-site
pnpm dev

Visit http://localhost:4321, and your first Astro site is up and running.

3.2 Project Structure#

my-site/
├── astro.config.mjs # Astro config file
├── src/
│ ├── pages/ # Route pages (file-system routing)
│ │ ├── index.astro # Home page /
│ │ ├── about.astro # /about
│ │ └── blog/
│ │ └── [slug].astro # Dynamic route /blog/xxx
│ ├── components/ # Reusable components
│ ├── content/ # Content collections (blog posts, etc.)
│ ├── layouts/ # Page layouts
│ └── styles/ # Global styles
├── public/ # Static assets (copied directly to build output)
└── package.json

4. Astro Component Syntax#

4.1 Basic Structure#

A .astro file consists of two parts: the component script (runs on the server) and the template (outputs HTML).

---
// Component script (fence): runs on the server/at build time
// Can import other components, call APIs, query databases
const name = "Astro";
const items = ["SSG", "Island Architecture", "Multi-framework"];
---
<!-- Template section: output HTML directly -->
<h1>Hello, {name}!</h1> <!-- Inline JS expressions inside { } -->
<ul>
{items.map(item => <li>{item}</li>)}
</ul>

Key difference: the code between --- runs on the server (at build time) and is not sent to the browser. The template portion outputs pure HTML.

4.2 Props and Slots#

components/Card.astro
---
export interface Props {
title: string;
subtitle?: string;
}
const { title, subtitle } = Astro.props;
---
<div class="card">
<h2>{title}</h2>
{subtitle && <p class="subtitle">{subtitle}</p>}
<!-- Slots: parent components can insert content -->
<slot />
</div>
<style>
/* Component-level styles, scoped by default */
.card {
padding: 1.5rem;
border-radius: 12px;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.subtitle {
color: #666;
font-size: 0.9rem;
}
</style>

Using this component:

---
import Card from '../components/Card.astro';
---
<Card title="Getting Started with Astro" subtitle="From Scratch">
<p>This is some content inserted into the slot.</p>
</Card>

4.3 Styles Are Scoped by Default#

Astro’s <style> tag is scoped by default—styles only apply to the current component and don’t leak into other parts of the page. This is a built-in capability; you don’t need CSS Modules or styled-components.

<!-- This h1 style only affects this component -->
<style>
h1 { color: red; }
</style>
<!-- If you need global styles -->
<style is:global>
:root {
--primary: #3b82f6;
}
</style>

5. Content Collections#

For content-centric sites like blogs and documentation sites, Astro provides content collections—a type-safe content management system.

5.1 Defining a Collection#

src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blogCollection = defineCollection({
type: 'content', // 'content' means Markdown/MDX
schema: z.object({
title: z.string(),
published: z.date(),
description: z.string().optional(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
});
export const collections = {
blog: blogCollection,
};

5.2 Writing Content#

src/content/blog/hello-world.md
---
title: Hello World
published: 2026-06-17
tags: [Astro, Blog]
---
This is my first Astro blog post.

5.3 Querying and Rendering#

src/pages/blog/index.astro
---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => {
return import.meta.env.PROD ? !data.draft : true;
});
// Sort by date
posts.sort((a, b) => b.data.published.valueOf() - a.data.published.valueOf());
---
<h1>Blog</h1>
<ul>
{posts.map(post => (
<li>
<a href={`/blog/${post.slug}`}>{post.data.title}</a>
<time>{post.data.published.toLocaleDateString()}</time>
</li>
))}
</ul>

6. Mixing Other Frameworks#

Astro’s most magical feature: you can use React, Vue, and Svelte components on the same page.

6.1 Adding Integrations#

Terminal window
# Add React support
pnpm astro add react
# Add Vue support
pnpm astro add vue
# Add Svelte support
pnpm astro add svelte

6.2 Using Framework Components in Astro#

pages/island-demo.astro
---
import ReactCounter from '../components/ReactCounter.jsx';
import VueTodoList from '../components/VueTodoList.vue';
import SvelteChart from '../components/SvelteChart.svelte';
---
<!-- Static content, Astro renders it directly to HTML -->
<h1>Multi-framework Demo</h1>
<p>The components below come from different frameworks:</p>
<!-- React component -->
<ReactCounter client:load />
<!-- Vue component -->
<VueTodoList client:visible />
<!-- Svelte component -->
<SvelteChart client:idle />

6.3 Client Directives: Controlling Hydration Timing#

Every framework component needs a client:* directive to determine when to load JS:

DirectiveBehaviorUse Case
client:loadHydrate immediately after page loadHigh-priority interactions (e.g., navigation menus)
client:idleHydrate when the browser is idleLow-priority interactions
client:visibleHydrate when the component enters the viewportInteractive components lower on the page
client:mediaHydrate when a media query matchesComponents that only load on mobile
client:onlySkip SSR, client-side rendering onlyComponents that depend on browser APIs
<!-- Example: loading different components at different times -->
<MobileMenu client:media="(max-width: 768px)" />
<SearchDialog client:idle />
<CommentSection client:visible />
<ThemeToggle client:load />

7. Routing and Page Modes#

7.1 SSR Mode (Default)#

Astro is SSG by default (generates pure HTML at build time). If you need SSR (dynamic rendering on each request), add this at the top of the file:

---
export const prerender = false;
---
<!-- This page renders dynamically on every request -->
<h1>Current time: {new Date().toLocaleTimeString()}</h1>

Or enable it globally in astro.config.mjs:

export default defineConfig({
output: 'server', // or 'hybrid' (hybrid mode)
});

7.2 Three Output Modes#

ModeDescriptionUse Case
static (default)Generates fully static HTML at build timeBlogs, documentation sites, corporate sites
serverRenders on the server on each requestSites that need dynamic data
hybridMost pages static, a few pages dynamicStatic blog + login/search functionality

8. Astro vs Other Frameworks#

DimensionAstroNext.jsNuxt
Core philosophyZero JS by default, island enhancementReact full-stack frameworkVue full-stack framework
Default renderingSSGSSGSSG
JS bundle sizeExtremely low (0KB by default)MediumMedium
Multi-framework support✅ Native support❌ React only❌ Vue only
Learning curveGentle (HTML-like templates)Steep (RSC, App Router)Medium
Best scenarioBlogs, docs, marketing sitesComplex full-stack appsComplex full-stack apps
Build speedExtremely fast (Go + Rust engine)MediumMedium

9. When to Choose Astro?#

Typical signals for choosing Astro:

ScenarioReason
Personal blogPurely static, zero JS, perfect Lighthouse score
Technical documentation siteContent-focused, minimal interaction
Company website / marketing pageFast first paint, high SEO requirements
E-commerce showcase pageProduct display-focused, interactions via islands
Micro-frontend aggregationDifferent teams use different frameworks, Astro unifies the output

Signals not to choose Astro:

ScenarioRecommended Alternative
Extremely complex frontend interactions (e.g., online Excel)React / Vue SPA
Need client-side state management across the entire siteNext.js / Nuxt
Team is already deeply invested in a particular framework ecosystemChoose a solution within that ecosystem

10. Summary#

Astro’s success comes from a simple observation: most websites’ “interactions” might just be a few buttons, but traditional frameworks load a heavy runtime for the entire site.

Its design philosophy can be summarized as:

Output HTML by default, inject JavaScript on demand. “Multi-framework” isn’t a gimmick—it solves a real problem: different teams and different scenarios can each use the tools they’re best at, and Astro ultimately unifies them into a static site with optimal performance.

If you want to build a blog, documentation site, marketing page, or any website where “content > interaction,” Astro may be the best choice right now. Just like this blog—every article you’re reading right now is pure HTML generated by Astro at build time, with no unnecessary JS.

Learning path suggestions:
1. Build a personal blog with Astro (3~5 days) → understand SSG and content collections
2. Add one or two React/Vue island components (1~2 days) → understand island architecture
3. Try hybrid mode and add search functionality (3~5 days) → understand dynamic routing
Getting Started with Astro: A Modern Content-First Web Framework
https://www.hehonglei.com/technology/astro-intro/
Author
HHLBlog
Published at
2026-06-17
License
CC BY-NC-SA 4.0