DEV Community

Cover image for How to Create Your Own Personal Blog with Astro
Thomas Bonnet
Thomas Bonnet Subscriber Community Curator

Posted on Edited on Originally published at thomasbnt.dev AI-assisted

How to Create Your Own Personal Blog with Astro

You want a personal blog without spending three months on it or paying for a CMS every month.
This site you’re reading right now is exactly that: an Astro blog.

An example of a post on my website under Astro

No database, no admin panel to maintain:

  • Markdown files
  • Content Collections
  • A bit of Tailwind
  • And a static deployment on Cloudflare Workers.

Here’s how to set it up, using the same building blocks as the ones used here.

A personal blog is a project you should be able to maintain on your own, for as long as you want. No heavy external dependencies, no CMS that disappears overnight, no server to run. Just a Git repository, Markdown files, and static hosting.

Why Astro for a blog

Astro itself explains its philosophy in “Why Astro”: a framework designed for content-focused sites, server-side by default. In practical terms, this means:

  • Simplicity: no state management, no JavaScript framework to learn just to write an article. .astro components and Markdown that’s it.
  • Speed: Astro renders static HTML by default; no JavaScript is sent to the client unless you explicitly request it (client:load, etc.). For a blog, this is exactly what you need: content that’s easy to read, not a SPA.
  • Fast deployment: static build, no servers to run, deploys to any CDN in seconds.
  • Markdown content: each post is a .md file, versioned in Git, editable in any text editor.
  • Easy to maintain: Content collections provide data typing for front matter using Zod, which is validated directly during the build a front matter error breaks the build rather than silently crashing in production.

Create the project

npm create astro@latest -- --template minimal
cd mon-blog
npm install
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

Once Tailwind and the content collections have been added (next steps), the directory structure of a minimal Astro blog looks like this:

my-blog/
├── astro.config.ts
├── package.json
├── tsconfig.json
└── src/
    ├── content.config.ts       # article structure (Zod)
    ├── styles/
    │   └── global.css          # import Tailwind
    ├── data/
    │   └── blog/
    │       ├── premier-article/
    │       │   ├── index.md
    │       │   └── banner.webp
    │       └── deuxieme-article/
    │           └── index.md
    ├── layouts/
    │   ├── Layout.astro        # <head>, meta, theme
    │   └── PostDetails.astro   # The rendering of an article
    ├── components/
    │   └── Header.astro
    └── pages/
        ├── index.astro
        └── blog/
            ├── index.astro         # List of Articles
            └── [...slug]/
                └── index.astro     # article page (dynamic route)
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

A deliberately simplified structure to get started: in a real project, you’ll quickly add a pagination route ([...page].astro) and quite a few other components as you go.

Add Tailwind (v4, via the Vite plugin, no need to manage a tailwind.config.js file):

npm install tailwindcss @tailwindcss/vite
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
// astro.config.ts
import { defineConfig } from "astro/config";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  vite: {
    plugins: [tailwindcss()],
  },
});
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

And a CSS entry point that imports Tailwind:

/* src/styles/global.css */
@import "tailwindcss";
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

Articles in Content Collections

Each article is stored in its own folder under src/data/blog/<slug>/, along with an index.md file and its images (banner.webp, screenshots, etc.).
This keeps each article self-contained: there’s no need to search through a shared public/images folder to find an asset.

The schema is declared once, in src/content.config.ts:

import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";

const blog = defineCollection({
  loader: glob({ pattern: "**/[^_]*.md", base: "./src/data/blog" }),
  schema: ({ image }) =>
    z.object({
      title: z.string(),
      description: z.string(),
      pubDatetime: z.date(),
      modDatetime: z.date().optional(),
      draft: z.boolean().default(true),
      tags: z.array(z.string()).default(["notes"]),
      ogImage: image().or(z.string()).optional(),
    }),
});

export const collections = { blog };
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

So an article is just that:

---
title: "My First Article"
description: "A short tagline for lists and SEO."
pubDatetime: 2026-08-10
draft: false
tags:
  - Astro
---

Markdown content here.
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

If draft is set to true or pubDatetime is in the future, the article should not appear in the listings:
This is a filter you must apply yourself when calling getCollection; Astro does not do this for you:

import { getCollection } from "astro:content";

const now = Date.now();
const posts = await getCollection("blog", ({ data }) => {
  const isPublished = now > data.pubDatetime.getTime();
  return !data.draft && isPublished;
});
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

Layouts: One for the list, one for the post

Two layouts are enough for the vast majority of personal blogs:

  • A base layout (Layout.astro) that sets the <head>, the OpenGraph meta tags, and the light/dark theme.
  • A PostDetails.astro that receives a collection entry, displays the title and dates, and renders the Markdown via render(post):
---
import { render } from "astro:content";
const { post } = Astro.props;
const { Content } = await render(post);
---

<article>
  <Content />
</article>
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

All typographic styling (headings, quotes, code) is handled by a Tailwind class like prose applied to this <article>,
not by hand-written CSS on a per-element basis.

Lightweight i18n: a dictionary, not a router

There’s no need for Astro’s native i18n routing if the site has only one content language and just interface labels to translate.
A simple dictionary is all you need:

// src/i18n/ui.ts
export const defaultLang = "fr";

export const ui = {
  fr: {
    "nav.home": "Accueil",
    "footer.rights": "Tous droits réservés",
  },
  en: {
    "nav.home": "Home",
    "footer.rights": "All rights reserved",
  },
} as const;
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
// src/i18n/utils.ts
import { ui, defaultLang } from "./ui";

export function useTranslations(lang: keyof typeof ui) {
  return function t(key: keyof (typeof ui)[typeof defaultLang]) {
    return ui[lang][key] ?? ui[defaultLang][key];
  };
}
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

In a component: const t = useTranslations(lang); t(“nav.home”).
Zero external dependencies, zero JSON translation files to load dynamically: everything is typed and validated during the build by TypeScript.

For the articles themselves, rather than having duplicate content in two languages, an optional englishUrl field in the front matter is enough to point to an English version published elsewhere (dev.to, for example), without having to maintain a complete translation that’s synchronized with every change.

Deploying to Cloudflare Workers

Astro in static mode generates a dist/ folder ready for hosting.
Cloudflare Workers can serve these static assets directly (no need for Pages) via Wrangler:

npm install -D wrangler
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
// wrangler.jsonc
{
  "name": "my-blog",
  "compatibility_date": "2026-01-01",
  "assets": {
    "directory": "./dist"
  }
}
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
npm run build
npx wrangler deploy
🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน 🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน

Each wrangler deploy command pushes assets to the Cloudflare edge.
For automated CI/CD (push = deployment), connect your Git repo in the Cloudflare Workers dashboard or add a wrangler deploy step to your pipeline (GitHub Actions, for example).

No servers to manage, no cold starts for static content.
Cloudflare’s CDN serves files directly from the edge, and you can get right back to writing.

Don’t want to start from scratch?

My website is actually based on an existing theme: AstroPaper, a minimalist, responsive, accessible, and SEO-friendly blog theme, which I then customized to my liking (Tailwind v4, lightweight i18n, TOC, etc.).

No need to build everything from scratch: the official Astro theme marketplace lists dozens of ready-to-use templates (blogs, portfolios, documentation, e-commerce) that you can clone and then customize.

Conclusion

Brick Role
Astro Generates static HTML; no JS by default
Content collections Markdown articles, validated by a Zod schema
Tailwind v4 Styling, with no configuration files to maintain
i18n dictionary Translated interface labels, without heavy routing
Cloudflare Workers Static hosting, one-command deployment

A personal blog with Astro has very few moving parts: Markdown files organized into folders, a Zod schema that keeps the front matter clean, Tailwind for styling, and a static site generator that pushes everything to a CDN. No operations to manage, nothing to run continuously, just write, commit, push.

Learn More

Top comments (11)

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
 
francistrdev profile image
FrancisTRᴅᴇᴠ •

oooo saving this for later when I have enough determination to do this. Thanks Thomas!

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
 
jarvisscript profile image
Chris Jarvis •

Is there anyway for readers to comment, like posts, or give feedback on posts?

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
 
thomasbnt profile image
Thomas Bonnet •

No, but can be a very nice feature!

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
 
karandpr profile image
Karan Gandhi •

Been a while since I read your posts @thomasbnt . Congrats on your job and great tutorial as always.

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
 
thomasbnt profile image
Thomas Bonnet •

Oh that a kind comment, thank you Karan!

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
 
ograynes profile image
Ograynes •

Thank you very much, I who plan to go back to computer science and who want a Blog

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
 
thomasbnt profile image
Thomas Bonnet •

Oh cool! Don't hesitate to share your blog here when it's published!

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
 
luke_bennett_ed52405c798e profile image
Luke Bennett • • Edited

Thats why im telling you the troth You pay for your new job this week. And you didnt tell us that why.
Hope you get a new job.

Thanks luke.
😄

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน Thread
 
thomasbnt profile image
Thomas Bonnet •

I have a new job yes!

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน Thread
 
luke_bennett_ed52405c798e profile image
Luke Bennett •

Greet

🔥 ข่าวด่วน: thomasbnt/how to create your own personal blog with astro dh - ประเด็นร้อน
 
luke_bennett_ed52405c798e profile image
Luke Bennett •

No thanks