Skip to content

Blogging with MkDocs

Warning

This post was written before the built-in blog plugin was released.

I love MkDocs - especially with the Material for MkDocs theme - and after accidentally killing my blog (yet again), I wanted to try and rebuild it with plain Markdown files and MkDocs.

There are a number of posts, queries, and plugins around using MkDocs for a blog instead of documentation. This time around I want to keep it simple - close to a vanilla installation, or at least with the expectation that things will still broadly work if one of the plugins breaks during an upgrade.

It's worth noting I'm already familiar enough with MkDocs to be comfortable with it, which is why this feels simple to me. It might not feel simple to someone else.

Info

Most of these notes are just a summary of the amazing docs on the Material for MkDocs site - you should definitely have a browse through, there's more than I could cover here.

MkDocs + Material for MkDocs

Start with a basic mkdocs.yml. MkDocs expects your source files to live in a subdirectory next to the mkdocs.yml (if this bother you, the mkdocs-simple-plugin is worth checking out).

I've called my directory src/, since they aren't really "docs" at this point:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
---
site_name: craig0990.co.uk
docs_dir: src/
site_dir: public/
copyright: |
  Copyright &copy; 2021 Craig Roberts<br>
  Content is licensed under the <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br>
  Illustrations sourced and modified from <a href="https://undraw.co">unDraw</a>.
theme:
  name: material
  icon:
    logo: material/code-tags
  features:
    - navigation.tabs
    - navigation.tabs.sticky

This sets up the basics:

  • The docs_dir pointing to our src/ directory
  • Copyright and attribution information
  • The material theme
  • A customised header logo
  • Enable tabs and make them sticky on scroll

At this point, because I'm not really a Python developer, I also have a simple requirements.txt:

1
2
mkdocs==1.1.2
mkdocs-material==7.1.8

To create the home page content, create either src/README.md or src/index.md according to your preference.

Post Order - Most Recent First

Blog posts live in src/posts. By default, MkDocs will sort alphanumerically by filename. ISO-8601 is well suited for sorting, so my blog posts are named as src/posts/${YYYY}-${MM}-${DD}-${article-slug}.md.

But blog posts are typically presented in reverse order - with the most recent post first.

The mkdocs-awesome-pages-plugin provides an order option that will do just that.

So it gets added to requirements.txt:

1
mkdocs-awesome-pages-plugin==2.5.0

And configured in mkdocs.yml:

1
2
3
plugins:
  - search
  - awesome-pages

(Once we add our own list of plugins, we have to also include the default search plugin explicitly)

And then we add a .pages file to src/posts/.pages:

1
order: desc

I've chosen to break down posts by year for a bit more organisation, so .pages is also repeated in the src/pages/2021/ directory, and will need to be repeated in other years.

If you're thinking about folder names broken down by ${YYYY}/${MM} - you can, but I found it tedious to use .pages to rename the folders to the month of the year. And I don't blog that often 🙃

Post Index - All Posts

For part of the home page illustration (which we'll get to in a minute), I wanted to be able to link to the posts/ page (even though it's already in the header tabs).

By default, MkDocs will select the first article in the navigation as the "index" for the posts/ page - which means I can't link to it without updating it every time I write a new post.

The simplest way around this is a src/posts/index.md, but that will just be an empty page.

The mkdocs-pagenav-generator fixes this - it works with the awesome-pages plugin to provide a {nav} placeholder in the Markdown. This is perfect for generating a list of all blog posts for the index page in src/posts/index.md:

# All Posts

{nav}

And adding it to the plugins list:

1
2
3
4
plugins:
  - search
  - awesome-pages
  - pagenav-generator

The only confusing part was the syntax for requirements.txt when pulling directly from GitHub - I prefer to pin dependencies to some kind of committish, and the repository isn't tagged, so it's a raw commit:

1
git+https://github.com/Andre601/mkdocs-pagenav-generator@b9cf3915#egg=mkdocs-pagenav-generator

This also makes the navigation sidebar rather redundant, so we'll customise that later.

That's the basics of a) MkDocs with a decent theme, b) blog posts in chronological order, and c) a post index.

Basic Customisation

I've gone a little bit further with help from the Material for MkDocs documentation:

And the mkdocs-git-revision-date-localized-plugin for adding created/last updated dates to the footer of the blog posts.

Customising the Landing Page

Customising the landing page is mostly documented in Material for MkDocs issues #2057 and #1996.

Mine looks something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{% extends "main.html" %}

{% block extrahead %}
    <link rel="stylesheet" href="/home.css">
{% endblock %}

{% block content %}

<div class="home">
{% include 'home.svg' %}
</div>

{% endblock %}
{% block footer %}{{ super() }}{% endblock %}

Which replaces the main index page for the site with an include of home.svg, which is adapted from the awesome illustrations at unDraw.

This could be done with a normal index.md - the difference here is the removal of the "next" and "previous" links above the footer.

That's All

As mentioned earlier, definitely check out the docs for Material for MkDocs - there's simply loads of goodies. Emojis, content tabs, and footnotes are some of my favourites, and versioning support with mike is brilliant for more technical docs, which I'll try and cover soon.

You can see the full setup at GitLab, including the CI/CD pipeline which isn't covered here.


Last update: September 27, 2023
Created: June 20, 2021