DEV Community

Cover image for How To Build A Personal Blog With MkDocs
RequieMa
RequieMa

Posted on • Originally published at requiema.github.io

How To Build A Personal Blog With MkDocs

How This Blog Was Built

The first post is about the blog itself — a write-up of every decision and step that went into putting this site together.

If you are setting up a similar lightweight developer blog with MkDocs Material, this should save you a couple of hours.

Why MkDocs Material?

I wanted three things:

  1. Write posts in Markdown — no CMS, no database, no WordPress.
  2. Deploy on every git push — GitHub Actions builds the site and deploys to GitHub Pages.
  3. Bilingual out of the box — English and Chinese, with search that handles both.

The blog plugin is first-class, not an afterthought.

Step-by-step Setup

1. Scaffold

uv manages dependencies with a lockfile (uv.lock) so builds are reproducible.

uv init
uv add mkdocs-material pillow cairosvg \
  mkdocs-git-revision-date-localized-plugin \
  mkdocs-rss-plugin
Enter fullscreen mode Exit fullscreen mode

After that, mkdocs.yml and docs/index.md are the only files you touch. Replace index.md with your landing page.

Local dev server:

uv run mkdocs serve    # → http://127.0.0.1:8000
Enter fullscreen mode Exit fullscreen mode

2. Core Configuration

mkdocs.yml is the single source of truth. Here is what goes in:

# Blog plugin — posts, archive, categories, pagination
plugins:
  - blog:
      blog_dir: blog
      post_dir: "{blog}/posts"
      archive: true
      categories: true
      pagination: true
      pagination_per_page: 10
      authors_file: "{blog}/.authors.yml"

  # Tags — auto-generates tag index pages
  - tags

  # Search with Chinese tokenization
  - search:
      lang: [en, zh]

  # RSS
  - rss:
      match_path: blog/posts/.*
Enter fullscreen mode Exit fullscreen mode

3. Bilingual Strategy

No separate site builds, no mkdocs-static-i18n plugin. Just a directory convention:

docs/blog/posts/
├── en/    ← English posts go here, tagged `english`
└── zh/    ← 中文文章放这里,tag `chinese`
Enter fullscreen mode Exit fullscreen mode

It may be time-consuming for writing articles in two different languages. However, I prefer a full control of the content I wrote.

Search indexes both languages — the lang: [en, zh] line above tells the built-in segmenter to handle CJK characters. Readers filter by language via the tag system.

4. GitHub Actions CI

A single workflow file (.github/workflows/ci.yml) does three things on every push to Master:

  1. Checkout the repo (full depth, needed for git-revision-date-localized)
  2. pip install dependencies + mkdocs build
  3. actions/deploy-pages to publish

Set Settings → Pages → Source → GitHub Actions in the repo and you are done.

5. Analytics & Comments

Google Analytics 4 — one block in mkdocs.yml:

extra:
  analytics:
    provider: google
    property: G-XXXXXXXXXX
Enter fullscreen mode Exit fullscreen mode

giscus — free, no-ads comment system powered by GitHub Discussions. Enable Discussions in the repo settings, install the giscus app, and drop the repo/category IDs into mkdocs.yml.

6. Theme Polish — Atom One Dark Pro

This site doesn't use the stock Material palette.

Colors are inspired by Atom One Dark Pro -- I always like this palette, and was really sad when Atom stopped updating 😿. All overrides live in a single CSS file (docs/stylesheets/extra.css) loaded via extra_css.

Dark mode (default):

Variable Hex Usage
Background #282c34 Page body
Surface #21252b Code blocks, sidebar
Text #abb2bf Body copy
Primary #61afef Links, header
Accent #56b6c2 Hover states

Light mode:

Variable Hex Usage
Background #fafafa Page body
Surface #ffffff Cards, sidebar
Text #383a42 Body copy
Primary #4078f2 Links, header
Accent #0184bc Hover states

Key CSS snippet:

[data-md-color-scheme="slate"] {
  --md-default-bg-color: #282c34;
  --md-primary-fg-color: #61afef;
  --md-accent-fg-color:  #56b6c2;
}

[data-md-color-scheme="default"] {
  --md-default-bg-color: #fafafa;
  --md-primary-fg-color: #4078f2;
  --md-accent-fg-color:  #0184bc;
}
Enter fullscreen mode Exit fullscreen mode

The home page gets a hero layout — large title, single-line subtitle, and Material's .md-button classes for CTA links. Blog post cards have rounded corners, subtle backgrounds, and a hover lift effect. A fadeIn animation smooths page transitions.

Dark mode is set as the default — the slate palette entry is listed first in mkdocs.yml.

Writing a Post

Every post is a Markdown file with frontmatter:

---
date:
  created: 2026-07-10
categories:
  - en             # language category: en or zh
  - topic-name
tags:
  - english     # or chinese
  - topic-tag
authors:
  - requiema
---

# Title

Content goes here.
Enter fullscreen mode Exit fullscreen mode

Drop it in docs/blog/posts/en/ or docs/blog/posts/zh/, commit, push — the CI pipeline handles
the rest.

Summary

Dimension Choice
SSG MkDocs Material 9.7
Hosting GitHub Pages (Actions CI)
Language en / zh via directory convention + tags
Search Built-in, lang: [en, zh]
Analytics Google Analytics 4
Comments giscus (GitHub Discussions)
RSS mkdocs-rss-plugin
Writing Markdown + YAML frontmatter

The full config is in the repo source.

Go write something~

Top comments (2)

Collapse
 
xulingfeng profile image
xulingfeng

Clean setup. The bilingual approach — directory convention over i18n plugin — is refreshingly simple. And the CJK search handling is a detail most tutorials skip.

The Atom One Dark Pro nod hit hard 😿 RIP Atom.

Question: any friction with uv + MkDocs for plugin compatibility, or has it been smooth across all the Material plugins you're running?

Collapse
 
requiema profile image
RequieMa

Thank you for your reply.

uv works for all my setups 😉