Learning Logs · · 3 min read
Learning Astro: fast sites with zero JS by default
Astro from installation: .astro pages, components + scoped styles, islands architecture (client:load vs client:visible), and content collections for markdown blogs.
Astro is the framework for sites with more content than interaction — landing pages, blogs, documentation, company profiles. Its principle is radical: zero JavaScript in the browser by default, and you “switch on” interactivity only in the components that need it (islands architecture).
1. Installation
Prerequisite: Node.js 20+.
npm create astro@latest my-site
# choose the "Empty" template to learn from scratch
cd my-site
npm run dev # open http://localhost:4321
2. Pages = .astro files
Every file in src/pages/ automatically becomes a route (index.astro → /, about.astro → /about):
---
// Everything between these fences runs ON THE SERVER at build time — it never reaches the browser.
const products = await fetch("https://api.example.com/products").then(r => r.json());
const title = "My Shop";
---
<html lang="en">
<body>
<h1>{title}</h1>
<ul>
{products.map(p => <li>{p.name} — {p.price.toLocaleString()}</li>)}
</ul>
</body>
</html>
The syntax looks like JSX, but the output is pure HTML — that fetch happens at build time, not in your visitor’s browser.
3. Components & props
---
// src/components/Card.astro
const { title, price } = Astro.props;
---
<article class="card">
<h3>{title}</h3>
<p>{price.toLocaleString()}</p>
</article>
<style>
/* automatically scoped — never leaks into other components */
.card { border: 1px solid #e4e4e7; padding: 1rem; }
</style>
---
import Card from "../components/Card.astro";
---
<Card title="Gayo Coffee" price={85000} />
4. Islands: interactive only where needed
Need a genuinely interactive React/Vue/Svelte component? Mount it as an “island”:
npx astro add react
---
import Cart from "../components/Cart.jsx";
---
<h1>This page is 100% static HTML...</h1>
<Cart client:load /> <!-- ...except this one island -->
The client:* directive decides when its JavaScript loads:
client:load— immediately on page openclient:visible— only when the component enters the viewport (my most-used)client:idle— when the browser is idle
Without the directive, even a React component renders to static HTML — zero JS shipped.
5. Managed markdown content (content collections)
Keep articles in src/content/blog/*.md, define their schema with Zod in src/content.config.ts, and Astro validates every article’s frontmatter at build time — a missing title = a failed build, not a silently broken page.
Tips from experience
- Astro wins in the same cases as this site’s approach (Hono SSR + HTMX): content first, JavaScript later. Measure with Lighthouse and compare against an SPA yourself.
- Start with
client:visible, notclient:load— most interactivity lives below the fold. - If every page needs per-request real-time data, Astro’s server mode (SSR adapter) works, but consider whether another framework fits better — Astro’s core strength remains fast static content.
Want something like this built for your business?