Recently, I switched the SSG for my blog from Hugo to Astro. Hugo is pretty fast, but the documentation always seems a bit messy and outdated, on the other hand, Astro feels more modern with its well-organized tutorials.
Astro Nano
For theme, I really like how Astro Nano being super lightweighted yet also with all the features I need. It retains Astro’s core features while optimizing for minimal bundle size and maximum performance. For content-heavy sites like blogs, this provides several advantages:
- Faster build times
- Smaller output files
- Simplified configuration
- Excellent performance metrics
Creating the Blog Structure
I structured my blog with the following directory layout:
my-nano-blog/
├── public/
│ ├── favicon.svg
│ └── assets/
├── src/
│ ├── components/
│ │ ├── Header.astro
│ │ ├── Footer.astro
│ │ └── PostCard.astro
│ ├── content/
│ │ └── blog/
│ │ ├── post-1.md
│ │ └── post-2.md
│ ├── layouts/
│ │ ├── BaseLayout.astro
│ │ └── PostLayout.astro
│ └── pages/
│ ├── index.astro
│ ├── blog/
│ │ └── [...slug].astro
│ └── about.astro
├── astro.config.mjs
├── package.json
└── tsconfig.json
Setting Up Content Collections
Astro’s content collections are perfect for managing blog posts. I configured them by creating a src/content/config.ts
file:
import { defineCollection, z } from 'astro:content';
const blogCollection = defineCollection({
schema: z.object({
title: z.string(),
description: z.string(),
publishDate: z.date(),
tags: z.array(z.string()).optional(),
image: z.string().optional(),
}),
});
export const collections = {
'blog': blogCollection,
};
Creating Blog Posts
With collections configured, I create blog posts as Markdown files in the src/content/blog/
directory:
---
title: Getting Started with Astro Nano
description: Learn how to build lightning-fast websites with Astro Nano
publishDate: 2025-02-20
tags: ['astro', 'web development', 'performance']
image: '/assets/astro-banner.jpg'
---
# Getting Started with Astro Nano
Astro Nano provides an ultra-lightweight foundation for building static websites...
## Key Benefits
1. **Minimal JavaScript** - Only send JS when absolutely necessary
2. **Content-focused** - Perfect for blogs and documentation sites
3. **Blazing Fast** - Incredible load times and performance metrics
Building the Blog Index Page
The homepage displays a list of blog posts:
---
// src/pages/index.astro
import { getCollection } from 'astro:content';
import BaseLayout from '../layouts/BaseLayout.astro';
import PostCard from '../components/PostCard.astro';
const posts = await getCollection('blog');
const sortedPosts = posts.sort(
(a, b) => b.data.publishDate.getTime() - a.data.publishDate.getTime()
);
---
<BaseLayout title="My Tech Blog">
<h1>Welcome to My Blog</h1>
<section class="posts-grid">
{sortedPosts.map(post => (
<PostCard post={post} />
))}
</section>
</BaseLayout>
Creating the Blog Post Template
Individual blog posts are rendered using a dynamic route:
---
// src/pages/blog/[...slug].astro
import { getCollection } from 'astro:content';
import PostLayout from '../../layouts/PostLayout.astro';
export async function getStaticPaths() {
const blogEntries = await getCollection('blog');
return blogEntries.map(entry => ({
params: { slug: entry.slug },
props: { entry },
}));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<PostLayout frontmatter={entry.data}>
<Content />
</PostLayout>
Deploying to Cloudflare Pages
Deploying the blog to Cloudflare Pages was straightforward:
- Push the code to a GitHub repository
- Log in to the Cloudflare dashboard
- Navigate to Pages and click “Create a project”
- Connect your GitHub repository
- Configure the build settings:
- Build command:
npm run build
- Build output directory:
dist
- Environment variables:
- NODE_VERSION: 18
- Build command:
After the initial setup, Cloudflare automatically builds and deploys the site on each push to the main branch.
Integrating with Editors
For Writing Blogs
As I mentioned in another post, Obsidian has been my primary note-taking tool for years, thus it’d be amazing to use it for writing all my blogs. There’s actually a very straightforward way to achieve this: symlinks. I just have to symlink the directories of those Markdown files to my Obsidian Vault, since it natively supports Markdown and file properties, I get to manage all the content in Obsidian. This way, not only can I use all of its handy features such as templating, but also it becomes much easier to reference my notes when writing blogs.
For Coding
Since Astro provides code files as *.astro
, a little setup is needed to get proper language support in Neovim.
For language parsing and code completion, we can simply add astro
in the configuration of Treesitter and LSP. For formatting, a combination of prettier
and a plugin called prettier-plugin-astro
is pretty much all it takes to do the job, except that we also need to add a .prettierrc
file and add:
{
"plugins": ["prettier-plugin-astro"]
}