Skip to main content
Exploring ideas, sharing knowledge
Hidden Peaks Unlocked!
Looks like you found the hidden peaks! Future posts are now visible.
Peaks Hidden Again
The future posts are hidden once more. You know how to find them again.
Featured image for post: Astro At The Edge

Astro At The Edge

6 min 1,173 words

If you’ve been following this blog, you know I recently moved to Cloudflare Pages. But that was only half the story. The bigger change happened under the hood: a complete rewrite from React to Astro.

This wasn’t a “shiny new framework” migration. It was a calculated move to solve real problems I’d been fighting for months.

The SEO Problem

Let’s talk about the elephant in the room: React SPAs are terrible for SEO.

My previous blog was a client-side rendered React application. Every page was a blank HTML shell that JavaScript had to hydrate. Search engines saw… nothing useful. Google’s crawler eventually processes JavaScript, but “eventually” isn’t a strategy when you’re competing for search rankings.

The symptoms were painful:

  • Articles took weeks to appear in search results
  • Rich snippets never worked properly
  • Social media previews showed generic metadata instead of article-specific content
  • The sitemap existed, but search engines couldn’t meaningfully crawl the content

I needed server-side rendering, or better yet, static HTML generation. Enter Astro.

The Performance Obsession

Here’s a metric that matters: Time to First Contentful Paint (FCP). It measures how long users wait before seeing actual content. For a React SPA, that timeline looks like:

  1. Download HTML shell (~1KB)
  2. Download JavaScript bundle (~200KB+ for React alone)
  3. Parse and execute JavaScript
  4. Fetch data (if needed)
  5. Render content

That’s a lot of steps before users see words on a screen. On mobile networks? Painful.

Astro flips this completely. The HTML arrives ready to read. No JavaScript required for content. Interactive islands load separately, only where needed. The difference is immediately perceptible.

React SPA vs Astro: Performance Characteristics

MetricReact SPAAstro Static
First Contentful Paint1.5-3s< 500ms
JavaScript Bundle200KB+ (React + app)0KB (content pages)
Time to InteractiveAfter hydrationImmediate
Lighthouse Performance60-7595-100
Core Web VitalsOften failingConsistently passing

RSS Done Right

Setting up RSS feeds in a React SPA requires workarounds. You need build-time scripts to generate XML, separate from your main application. It’s doable but awkward.

Astro treats RSS as a first-class citizen. With the @astrojs/rss package, generating feeds is trivial:

import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';

export async function GET(context) {
  const posts = await getCollection('posts');
  return rss({
    title: 'My Blog',
    description: 'Technical articles',
    site: context.site,
    items: posts.map((post) => ({
      title: post.data.title,
      pubDate: post.data.publishedDate,
      link: `/posts/${post.slug}/`,
    })),
  });
}

That’s it. Both RSS and Atom feeds, generated at build time, always in sync with your content. No manual maintenance.

Sitemap Generation

Similarly, sitemap generation is built-in. The @astrojs/sitemap integration automatically discovers all your pages and generates a proper sitemap.xml. Search engines can now crawl your entire site structure without guessing.

For a content-driven site, this is table stakes. Astro makes it effortless.

The Template That Started It All

I didn’t build this blog from scratch. I found an excellent template from David Kimball that provided a solid foundation. Huge thanks to him for open-sourcing his work.

But here’s the interesting part: this became an experiment in brownfield agentic coding.

Most AI coding demonstrations show greenfield projects—starting from nothing, generating boilerplate. That’s the easy case. The hard case is taking an existing codebase you didn’t write and adapting it to your needs. You need to:

  • Understand existing architecture decisions
  • Identify which components to keep, modify, or replace
  • Maintain consistency with established patterns
  • Avoid breaking existing functionality while adding new features

This blog became my testing ground for Claude Code in exactly this scenario. The result? I was able to customize extensively while keeping the template’s solid foundation. The experience convinced me that agentic coding shines brightest when you’re extending existing work, not just generating from scratch.

What This Blog Offers Now

The migration wasn’t just about fixing problems—it was an opportunity to add features I’d always wanted. Here’s what you get:

Reading Experience

  • Dark/Light mode toggle: Your eyes will thank you during late-night reading sessions
  • Custom themes: Try Ctrl+. to open the theme selector. Yes, there are multiple color schemes
  • Table of Contents: Every article has a navigable outline
  • Advanced search: Press Ctrl+K to search across all content instantly

Content Organization

  • Topics on homepage: Posts grouped by category, not just reverse chronological order
  • Epic stories: Multi-part series with dedicated UI to follow the narrative
  • Concepts: A new content type for my opinions on technologies and practices, designed to stay evergreen

Knowledge Graph

This is the feature I’m most excited about: an Obsidian-style knowledge graph.

Every post links to other posts and concepts. Those connections are visualized as a graph you can explore. You’ll find a local graph on each page showing immediate connections, and a global graph showing the entire knowledge structure.

This mirrors how I think about technical topics—not as isolated articles, but as interconnected ideas that reinforce each other. The graph makes those connections visible and navigable.

Developer Features

  • Better image management: Optimized images with proper responsive srcsets
  • RSS and Atom feeds: Subscribe however you prefer
  • Sitemap generation: Search engines can discover everything
  • Custom MDX components: Callouts, comparison matrices, decision trees, code groups—all the components you see in this article
Easter Eggs?

There might be some hidden surprises scattered around. Have fun exploring.

The Technical Stack

For the curious, here’s what powers this blog:

  • Astro v6: The content framework
  • Cloudflare Workers: Edge hosting with global distribution
  • MDX: Markdown with embedded components
  • pnpm: Fast, disk-efficient package management
  • TypeScript: Type safety throughout
  • Pagefind: Client-side search with zero backend

The entire site builds in under a minute and deploys globally in seconds. Content changes are live within minutes of committing.

The Trade-offs

No migration is without costs. Here’s what I gave up:

Client-side routing feel: React SPAs have instantaneous navigation between pages. Astro pages are full page loads (though View Transitions API helps significantly).

Component ecosystem: React has a massive component library ecosystem. Astro’s is smaller. For a blog, this matters less—I mostly need typography and layout, not complex UI widgets.

Cloudflare integration stability: As noted in my Cloudflare concept, the Workers integration isn’t fully stable yet. CPU time varies wildly, and hitting compute limits on the free tier happens unpredictably.

For a content-driven site, these trade-offs are worth it. The SEO and performance gains far outweigh the downsides.

What’s Next

The migration is complete, but the blog keeps evolving. I’m planning:

  • More concepts covering technologies I use daily
  • Epic series on complex topics that deserve multi-part treatment
  • Better mobile experience (ongoing refinement)
  • More interactive MDX components as needed

The foundation is solid. Now it’s about filling it with content worth reading.

Final Thoughts

If you’re running a content-heavy site on a React SPA and fighting SEO battles, consider Astro seriously. The framework is mature, the developer experience is excellent, and the performance gains are real.

The Islands architecture is the key insight: most content doesn’t need JavaScript. Serve HTML, add interactivity surgically, and your metrics improve across the board.

Welcome to the new blog. Same writer, better platform.


Want to try Astro yourself? Check out their official documentation. For Cloudflare deployment, see my migration guide.

Found a bug or have feedback? Reach out on GitHub or LinkedIn.

Share this article