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 (1)

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?