Building Digital Garden with Astro
Table of Contents
I’ve wanted a personal space on the internet for a while. Not a social media profile, not a portfolio template — something that feels like mine. A place where I can write about things I’m learning, trails I’ve hiked, meals I’ve cooked, and the occasional parenting revelation. A digital garden, if you will.
This is the story of how Digital Garden came to be, and why I chose Astro to build it.
Why a static site?
Before picking any framework, I had to answer a more basic question: what kind of site do I actually need?
The answer was simple. I write content in Markdown. The content doesn’t change between visits. There’s no user authentication, no database, no shopping cart. Every visitor sees the same thing. This is a textbook case for a static site generator — take some Markdown files, run them through a build step, and output plain HTML, CSS, and a sprinkle of JavaScript.
Static sites are fast. They’re cheap to host (often free). They’re secure by default because there’s no server to compromise. And they age well — a static HTML file from 2005 still works perfectly today.
So: static site generator it is. But which one?
The landscape
There’s no shortage of options. I’ve looked at quite a few over the years:
Jekyll was the original GitHub Pages darling. Ruby-based, mature, battle-tested. But the Ruby ecosystem felt like friction I didn’t want — managing gems, dealing with version conflicts. It also hasn’t evolved much in recent years.
Hugo is blazingly fast and written in Go. Its build times are legendary. But its templating language always felt unintuitive to me. The learning curve for anything beyond basic layouts is steep, and the documentation assumes a lot of prior context.
Next.js is powerful, but it’s a full React framework. For a content-driven blog with zero interactivity requirements, it felt like bringing a forklift to move a chair. The build output ships React runtime code to the browser even when you don’t need it. Server components in Next.js 13+ help with this, but the complexity budget is high.
Gatsby was once the go-to React static site generator, but it’s effectively in maintenance mode now. The GraphQL data layer was clever but added cognitive overhead for simple use cases.
11ty (Eleventy) is wonderful — lightweight, flexible, JavaScript-based. It was my runner-up. But I wanted something with first-class TypeScript support and a more opinionated project structure out of the box.
And then there’s Astro.
What is Astro?
Astro is a web framework designed for content-driven websites. It was created by Fred K. Schott and the team behind Snowpack (an early ES modules-based build tool). The first public release came in mid-2021, and it’s been on a remarkable trajectory since.
The core philosophy is simple: ship less JavaScript. By default, Astro sends zero JavaScript to the browser. Your pages are rendered entirely to static HTML at build time. When you do need interactivity — a theme toggle, a search modal — you opt in explicitly.
This is what the Astro team calls the Islands Architecture. The page is a sea of static HTML, and interactive components are isolated “islands” that hydrate independently. You can even choose when an island hydrates: on page load, when it becomes visible, when the browser is idle, or only on certain media queries.
A brief history
- 2021 — Astro launches publicly. The pitch: a static site builder that lets you use any UI framework (React, Vue, Svelte, Solid) but ships zero client JavaScript by default.
- 2022 — Astro 1.0 releases. Content Collections arrive, giving Markdown and MDX files type-safe schemas.
- 2023 — Astro 2.0 and 3.0 bring hybrid rendering (mix static and server-rendered pages), View Transitions for SPA-like navigation, and image optimisation built in.
- 2024 — Astro 4.0 and 5.0 introduce the Content Layer API (a unified way to source content from files, APIs, or CMSs) and significant performance improvements.
- 2025 — Astro 6.0 lands with even more refinements. This site runs on Astro 6.
What stands out is the pace of iteration without breaking changes. Each major version has felt like a natural evolution rather than a rewrite.
Why I chose Astro
A few things tipped the scale:
Content Collections are brilliant
Astro treats content as a first-class concept. I define a schema for my blog posts using Zod:
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
heroImage: z.optional(image()),
tags: z.array(z.string()).default([]),
}),
If I forget a required field in a post’s frontmatter, the build fails with a clear error. If I pass a string where a date is expected, TypeScript catches it. The content is validated, typed, and queryable — getCollection('blog') returns fully typed data I can sort, filter, and render with confidence.
File-based routing that makes sense
Every .astro file in src/pages/ becomes a route. src/pages/blog/index.astro becomes /blog/. Dynamic routes use bracket syntax: src/pages/blog/[...slug].astro generates a page for each blog post. No routing configuration file, no magic — just files in folders.
The .astro component format
Astro has its own component format, and it’s one of my favourite things about the framework. The frontmatter fence (---) at the top is server-side JavaScript/TypeScript that runs at build time. Everything below is your template:
---
// This runs at build time — fetch data, import components, compute values
const posts = await getCollection('blog');
const sorted = posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---
<!-- This is the HTML template -->
<ul>
{sorted.map(post => (
<li><a href={`/blog/${post.id}/`}>{post.data.title}</a></li>
))}
</ul>
<style>
/* Scoped styles — only affect this component */
ul { list-style: none; }
</style>
No useState, no useEffect, no lifecycle methods. It’s just data fetching at the top and HTML at the bottom. Styles are scoped by default. It feels like writing HTML with superpowers.
Zero JavaScript by default
The theme toggle and search modal on this site need JavaScript, so those bits are explicitly interactive. Everything else — the blog listing, the post rendering, the RSS feed, the image optimisation — is pure static HTML. The result is fast page loads and excellent Lighthouse scores without any performance tuning.
How the site is structured
I settled on a folder-per-post structure for blog content:
src/content/blog/
building-digital-mind-with-astro/
index.md ← the post you're reading
first-post/
index.md
hero.jpg ← colocated assets
markdown-style-guide/
index.md
hero.jpg
Each post lives in its own folder. The folder name becomes the URL slug. Images and other assets sit alongside the Markdown file they belong to. No hunting through a shared images/ directory wondering which file belongs to which post.
The site generates a few things automatically at build time:
- RSS feed with full post content at
/rss.xml - Sitemap at
/sitemap-index.xml - Search index via Pagefind, with a search modal accessible from any page
- Optimised images via Sharp — responsive sizes, modern formats like WebP
- Tag pages at
/blog/tags/tech/,/blog/tags/writing/, etc.
Features I’m happy with
Dark mode that respects system preference and remembers your choice. The theme initialises before first paint to avoid the dreaded flash of wrong theme.
Reading time estimates calculated from word count, shown on the blog listing and individual posts.
Table of contents auto-generated from headings, collapsible so it doesn’t dominate shorter posts.
Full-text search powered by Pagefind. It indexes the static HTML at build time and runs entirely client-side — no server, no external service. The search modal opens with Cmd+K.
Translation support via Google Translate for Simplified and Traditional Chinese readers. Not as good as hand-written translations, but it makes the content accessible to a much wider audience with zero ongoing effort.
Code blocks with copy buttons that appear on hover. Small touch, but useful for a tech blog.
What I’d tell someone starting today
If you’re building a content-driven site — a blog, documentation, a marketing page, a digital garden — Astro is worth a serious look. The learning curve is gentle if you know HTML, CSS, and a bit of JavaScript. The documentation is excellent. The community is active and helpful.
Start with the blog template (npm create astro@latest -- --template blog), strip out what you don’t need, and build up from there. Write your content in Markdown, let Astro handle the rest.
The framework gets out of your way and lets you focus on what matters: the writing.