webdev

Why I Built My Site with Astro (And What I Learned)

Published: September 10, 2026

⏱️ 8 min read | 📝 1444 words

Why I Built My Site with Astro (And What I Learned)

1. Introduction

I’ll be honest: putting together a personal website was something I’d been putting off for way too long. When you’re someone who likes to build things—whether that means writing code, designing user interfaces, putting together complex LEGO sets, or diving into weekend DIY projects—you eventually run into a dilemma. You want a single home on the web that can fit all of it without feeling cluttered or over-engineered.

Every time I considered getting started, I ran into the same familiar headache: framework fatigue. Modern web development has a habit of making simple things unnecessarily complicated. I didn't need a massive client-side framework pushing megabytes of JavaScript down the wire just to display some written thoughts, project logs, and build photos. On the flip side, pure static site generators from a decade ago felt too rigid when I wanted to add interactive bits, like custom UI widgets or game demos.

That was until I finally gave Astro a shot. It promised a zero-JavaScript-by-default architecture while still letting me build with the modern component workflows I actually enjoy using. Here is the story of why Astro turned out to be the exact tool I needed for my digital garden, how it handles a mix of totally different topics, and what I picked up along the way.


2. Why Astro over Other Options?

Zero JavaScript by Default

The core pitch that sold me on Astro is its "Islands Architecture". Instead of taking your entire web page and wrapping it in a single giant JavaScript application (the way traditional Single Page Applications do), Astro renders every page to plain, fast HTML and CSS by default.

If you want an interactive piece of UI—say, a theme toggle button or an image gallery—you treat that specific piece as an "island" of interactivity floating in a sea of static content. Astro only ships JavaScript for that exact component, and only when you explicitly tell it to. For a content-heavy site featuring project updates and articles, this keeps page loads lightning-fast without sacrificing cool dynamic elements.

Framework Agnostic Freedom

Most web frameworks force you into their specific ecosystem. If you choose Next.js, you're writing React. If you choose Nuxt, you're in Vue land. Astro doesn't care.

It lets you drop in components built with React, Svelte, Vue, or solid vanilla HTML side-by-side in the exact same project. If I build a quick game prototype in Svelte or a custom UI widget in React, I can drop them straight into an Astro post without rebuilding my entire setup or hauling around massive bundle overhead.

Built-in Content Collections

Because my goal was to host everything from web dev tutorials to LEGO build logs and physical DIY projects, keeping content organized was a top priority. Astro’s Content Collections feature felt like a cheat code.

It allows you to define structured Markdown or MDX files with type-safe schema validation using Zod. I can tag posts with #webdev, #lego, #diy, or #games, and Astro automatically checks to ensure my frontmatter metadata (titles, dates, tags, preview images) is correct before the site even builds. It gives you all the structure of a headless CMS directly inside your local code editor.

Out-of-the-Box Performance

We’ve all spent hours trying to optimize front-end build pipelines—setting up image compression, minifying scripts, lazily loading assets, and tuning cache settings just to get a decent Lighthouse score.

With Astro, high performance is essentially the default baseline. Because the generated output is lightweight static HTML, page speed scores land near 100 straight out of the gate without needing a stack of optimization plugins. That meant I could spend less time tweaking performance configurations and more time actually building projects and writing content.


3. Key Site Architecture & Features

When setting up a site designed to handle everything from web dev tutorials to physical DIY builds, keeping the file structure clean and scalable is half the battle. Astro’s file-based routing and intuitive folder conventions make organizing a multi-topic site straightforward.

  • File-Based Routing: Pages are generated automatically based on the directory structure inside src/pages/. A file at src/pages/index.astro becomes your homepage, while src/pages/blog/[...slug].astro dynamically handles individual post URLs.
  • Layout Isolation: Reusable wrapper components (like BaseLayout.astro) handle global concerns—HTML metadata, Open Graph tags for social sharing, global CSS, and navigation bars—so post templates remain focused purely on content.
  • Component-Driven Styling: Pairing Astro with Tailwind CSS provides utility-first styling directly within component files, keeping visual tweaks scoped without ballooning your global CSS bundle.
  • Flexible Tagging & Categorization: Content Collections structure metadata cleanly, enabling easy filtering by topics (#webdev, #lego, #diy, #games) across index pages and post feeds.

4. Code Snippets & Implementation Examples

A. Content Collections Schema (src/content/config.ts)

To ensure every article has the required metadata—regardless of whether it's a technical deep dive or a weekend LEGO log—you can define a type-safe schema with Zod:

import { defineCollection, z } from 'astro:content';

const blogCollection = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.date(),
    updatedDate: z.date().optional(),
    tags: z.array(z.string()), // e.g., ['webdev', 'lego', 'diy', 'games']
    featured: z.boolean().default(false),
    heroImage: z.string().optional(),
  }),
});

export const collections = {
  'blog': blogCollection,
};

B. Selective Hydration with Astro Islands (src/components/InteractiveWidget.astro)

Astro lets you drop framework components into static HTML. By using client directives like client:visible, the JavaScript for interactive elements loads only when the user scrolls them into view:

---
// Import a framework component (e.g., React or Svelte) inside an Astro page
import DarkModeToggle from '../components/DarkModeToggle.jsx';
import HeaderNav from '../components/HeaderNav.astro';
---

<header class="flex justify-between items-center p-4 border-b border-neutral-800">
  <HeaderNav/>
  
  <!-- Hydrates only when visible in the viewport -->
  <DarkModeToggle client:visible/>
</header>

C. Fetching & Filtering Posts by Tag (src/pages/index.astro)

Querying and rendering content directly inside Astro templates requires minimal boilerplate:

---
import { getCollection } from 'astro:content';
import BaseLayout from '../layouts/BaseLayout.astro';

// Fetch all non-draft posts and sort by date
const allPosts = await getCollection('blog');
const sortedPosts = allPosts.sort(
  (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
);
---

<BaseLayout title="Home">
  <section class="max-w-3xl mx-auto py-8">
    <h1 class="text-3xl font-bold mb-6">Latest Projects & Notes</h1>
    <ul class="space-y-4">
      {sortedPosts.map((post) => (
        <li class="border-b border-neutral-800 pb-4">
          <a href={`/blog/${post.slug}`} class="text-xl font-semibold hover:underline">
            {post.data.title}
          </a>
          <p class="text-neutral-400 text-sm mt-1">{post.data.description}</p>
          <div class="flex gap-2 mt-2">
            {post.data.tags.map((tag) => (
              <span class="text-xs bg-neutral-800 px-2 py-1 rounded">#{tag}</span>
            ))}
          </div>
        </li>
      ))}
    </ul>
  </section>
</BaseLayout>

5. What I Learned & Challenges Faced

Building with Astro was largely a smooth experience, but transitioning to an island-based model comes with a minor mental shift and a few lessons worth sharing:

  • Understanding Hydration Directives: It’s easy to accidentally over-hydrate or under-hydrate components. Learning when to use client:load (for critical immediate interactions), client:visible (for off-screen widgets), or client:only (when SSG window object conflicts occur) took a little experimentation.
  • Managing Dynamic State Across Islands: Because Astro islands are isolated components floating in static HTML, passing state between two separate React or Svelte islands on the same page isn't as trivial as wrapping everything in a top-level React Context. Utilizing lightweight state management tools like Nano Stores made cross-island communication seamless without adding heavy runtime bloat.
  • Asset Handling with astro:assets: For a site featuring high-res photos of LEGO builds and DIY physical projects, image performance is critical. Astro’s built-in image optimization pipeline automatically converts, resizes, and compresses photos during build time, saving hours of manual image processing.
  • Seamless Vercel Deployment: Pushing the site live was virtually friction-free. Connecting the GitHub repository to Vercel provided instant build previews on every pull request, allowing me to test new post layouts and component tweaks before going live.

6. Conclusion & What’s Next

Taking a step back, Astro turned out to be the ideal foundation for this site. It bridges the gap between static content and dynamic capability without forcing you to choose between lightning-fast page speeds and modern developer tooling.

Most importantly, it eliminated the friction of getting started. Whether I’m publishing a front-end UI guide, logging a weekend LEGO set build, sharing a mini-game prototype, or writing up a physical DIY project, the setup stays out of the way and lets me focus on the actual building process.

If you’ve been putting off creating your own corner of the web because existing frameworks feel too heavy or overly complex, I highly recommend giving Astro a try.


What's Next? Now that the foundation is set, I'll be fleshing out draft articles, refining custom UI components, and documenting ongoing projects. Take a look around the site, filter by your favorite tags, and stay tuned for more builds coming soon!