Astro Blog Retrospective: Building and Expanding
Why Astro?
When I decided to rebuild my blog, I wanted something that stayed out of my way. Astro caught my attention because it ships zero JavaScript by default. You only add JS where you actually need it. On top of that, it separates content from framework overhead, so pages load fast and adding new posts is just dropping in a markdown file.
It lets me pick and choose frameworks for specific parts of the site, or use none at all. That flexibility was the selling point for me.
Blog Post Previews
One of the first things I wanted to get right was how posts are displayed. I built a BlogPostPreviewCard.astro component that handles the card layout, image, hover effects, and tags:
---
// BlogPostPreviewCard.astro (relevant excerpt)
import TagList from './TagList.astro';
import type { CollectionEntry } from 'astro:content';
import { Image } from 'astro:assets';
// Import placeholder images
import placeholder1 from '../images/blog-placeholder-1.jpg';
import placeholder2 from '../images/blog-placeholder-2.jpg';
// ...other imports
interface Props {
post: CollectionEntry<'blog'>;
}
const { post } = Astro.props;
const { title, description, heroImage, tags = [] } = post.data;
// Map hero image paths to the imported image objects
const heroImageMap = {
'/blog-placeholder-1.jpg': placeholder1,
'/blog-placeholder-2.jpg': placeholder2,
// ...other mappings
};
// Get the appropriate image based on the heroImage path
const imageSource = heroImage ? heroImageMap[heroImage as keyof typeof heroImageMap] : undefined;
---
<div class="card">
<div class="image-container">
{imageSource && (
<Image
src={imageSource}
alt={description || title}
width={600}
height={400}
class="card-image"
quality={80}
/>
)}
<div class="image-overlay">
<div class="read-indicator">
<a href={`/blog/${post.slug}/`} class="button-link">Read Post</a>
</div>
</div>
</div>
<!-- Content section with title, description, and tags -->
</div> A few things worth pointing out here:
<Image>fromastro:assetshandles automatic image optimization once you wire up the imports correctly.- There’s a hover overlay that reveals a “Read Post” link. Small detail, but it makes the cards feel more interactive.
- The
TagListcomponent ties in so I can categorize and filter posts by topic.
The Image Optimization Gotcha
So here’s something that tripped me up. The default Astro blog template I started from used plain <img> tags. No optimization, no WebP conversion, nothing. The images were just served as-is.
I assumed that since Astro has built-in image optimization through astro:assets, the starter template would use it. It didn’t.
The Fix
Three steps to get it working:
// Step 1: Import the images
import placeholder1 from '../images/blog-placeholder-1.jpg';
import placeholder2 from '../images/blog-placeholder-2.jpg';
// ...
// Step 2: Create a mapping from string paths to imported images
const heroImageMap = {
'/blog-placeholder-1.jpg': placeholder1,
'/blog-placeholder-2.jpg': placeholder2,
// ...
};
// Step 3: Use the imported image with Astro's Image component
const imageSource = heroImage ?
heroImageMap[heroImage as keyof typeof heroImageMap] :
undefined;
// Later in the JSX:
{imageSource && (
<Image
src={imageSource}
alt={description || title}
width={600}
height={400}
class="card-image"
quality={80}
/>
)} With this in place, images get optimized at build time, you get automatic WebP conversion, and the browser downloads properly sized images instead of the full originals. A pretty big win for what ended up being a small change.
Where Things Stand
I’m happy with how the blog turned out. Pages load fast, writing new posts is simple, and the component setup keeps things flexible:
- File-based routing for new posts. Just add a file, it’s a page.
- Custom components like
CodeBlock.astroandTagList.astrothat I can drop into any post. - Dark mode toggle via a
ThemeToggle.astrocomponent. Nothing fancy, just a clean switch.
What’s Next
A few things I want to explore:
- Tag pages - Right now tags are just labels. I want to build proper tag index pages so you can browse by topic.
- Better image handling - Lazy loading, more aggressive optimization for larger images.
- RSS feed and comments - So people can subscribe or actually respond to posts.
- Some lightweight analytics - Just to see what’s getting read and confirm things are loading fast in the real world.
Wrapping Up
Moving to Astro has been a good decision. Minimal JavaScript, file-based routing, easy content management. I spend more time writing and less time fighting the framework.
If you’re thinking about a similar setup, just know that the starter templates don’t always have everything configured. Image optimization, for example, needed manual wiring. But once it’s set up, it just works. And Astro is genuinely refreshing compared to heavier frameworks where you’re constantly managing build configs and bundle sizes.