Article

How to Build a Blog with Astro

· 3 min read

Why Astro?

Among the many static site generators, Astro has several unique advantages:

  • Zero client JS by default: No JavaScript is sent to the browser unless you explicitly opt in
  • Component-based: Supports React, Vue, Svelte, and other framework components
  • Content-driven: Content Collections provide type-safe content management
  • Performance-first: Generated pages are small and load fast

Quick Start

Initialize the Project

# Create a new project
npm create astro@latest my-blog

# Enter the project directory
cd my-blog

# Install dependencies
npm install

# Start the dev server
npm run dev

Project Structure

my-blog/
├── src/
│   ├── components/     # UI components
│   ├── content/        # Content collections
│   │   └── blog/       # Blog posts (Markdown)
│   ├── layouts/        # Page layouts
│   ├── pages/          # Route pages
│   └── styles/         # Global styles
├── public/             # Static assets
├── astro.config.mjs    # Configuration
└── package.json

Content Collections

Astro’s Content Collections are the best way to manage blog content.

Define a Schema

// src/content.config.ts
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog };

Create a Post

Create a Markdown file under src/content/blog/:

---
title: "My First Post"
description: "This is a test post"
pubDate: 2026-07-04
tags: ["hello", "first-post"]
---

## Body Content

Write your article content here...

Query Content

---
import { getCollection } from 'astro:content';

const posts = (await getCollection('blog'))
  .filter(post => !post.data.draft)
  .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---

{posts.map(post => (
  <article>
    <h2><a href={`/${post.id}`}>{post.data.title}</a></h2>
    <p>{post.data.description}</p>
  </article>
))}

Dynamic Routes

Generate individual pages for each post:

---
// src/pages/[...slug].astro
import { getCollection, render } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map(post => ({
    params: { slug: post.id },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await render(post);
---

<article>
  <h1>{post.data.title}</h1>
  <Content />
</article>

Styling

Astro supports multiple styling approaches. CSS Variables are recommended for theme switching:

:root {
  --color-primary: #0369a1;
  --bg-primary: #ffffff;
  --text-primary: #0f172a;
}

html.dark {
  --color-primary: #38bdf8;
  --bg-primary: #0f172a;
  --text-primary: #f8fafc;
}

Use scoped styles inside components:

<style>
  .title {
    color: var(--color-primary);
    font-size: 2rem;
  }
</style>

Deployment

Build

npm run build

Output goes to the dist/ directory — pure static files.

Deployment Platforms

Platform Command Notes
Vercel vercel deploy Zero config, auto-detects Astro
Netlify Connect Git repo Auto build and deploy
GitHub Pages GitHub Actions Free, great for open source
Cloudflare Pages Connect Git repo Global CDN

Vercel Deployment Example

# Install Vercel CLI
npm i -g vercel

# Deploy
vercel deploy --prod

Conclusion

Astro is an ideal choice for building a blog: great developer experience, excellent generated performance, and convenient content management. If you want a fast, modern, and easy-to-maintain blog, Astro is worth trying.


Good tools let you focus on the content itself, not on wrestling with the toolchain. Astro is exactly that kind of tool.

Join the conversation

Comments