Skip to content

#260: Helpful Resources for MkDocs

With the common problems out of our way, we can now explore some helpful resources for MkDocs that makes writing blog posts a lot simpler.

The official documentation

It is easy to forget that the official documentation of Material for MkDocs and the one for MkDocs itself offer many helpful tips. Before you try to search the web, go there and use the search function. Most features are covered with a clear and understandable documentation that answers all your questions.

The Markdown Guide

If you are unsure about the Markdown syntax of a specific feature, go to markdownguide.org. They offer a great cheat sheet with the most often used parts and a more detailed explanation page on the whole syntax of Markdown.

Language names for the code highlighter

MkDocs uses Pygments for syntax highlighting and comes with an extensive documentation that covers all supported languages. The languages I use most are these here:

Language Name in code section
Python python, py
Python Console pycon, python-console
C# csharp, c#, cs
CSS css
HTML html
YAML yaml
JavaScript javascript, js
Markdown markdown, md

Images and Icons

MkDocs Material comes with these libraries for images and icons:

In the official documentation we get a built-in search feature to find the right icons. Should that not be enough, we can go to GitHub where the icons are in the mkdocs-material/material/templates /.icons folder.

Explicitly mark your drafts

With Material for MkDocs, we get two options to mark a post as a draft:

  • Explicit: with draft: true in the metadata
  • Implicit: set a publication date in the future and use the option draft_if_future_date: true in mkdocs.yaml

While the implicit method looks like less work, it limits us more in the way we can publish our post. Especially when we need to set a time to satisfy the RSS feed plug-in, we can only build the site with our new post after the time we declared in our post. Before that the post is treated as a draft and will not show up in the generated site.

The explicit approach allows us to decide when we want to build the site with our new post. All we need to do for that is to delete the one line that marks it as a draft. We can do that as long before the declared publication date as it fits our needs to prepare the site. I switched to this approach and since then enjoy the additional control I have.

Create slugs to name your Markdown files or directories

I want to use the slug for my posts as the directory name where I put the Markdown file and for the Markdown file itself. But how can I get the slug before I have the rendered post? A simple trick is to use the same function that Material for MkDocs uses to generate the slug in our own script:

1
2
3
4
5
6
7
8
9
import sys
from pymdownx import slugs

if __name__ == "__main__":
    args = sys.argv[1:]
    title = args[0]

    slug = slugs._uslugify(title, sep="-", case="lower")
    print(slug)

When I run this script, I can pass the title and get the slug back:

python make_slug.py "#260: Helpful Resources for MkDocs"

260-helpful-resources-for-mkdocs

Overwrite the slug

Especially when you move from another blogging software to MkDocs, you may have posts which now get a new URL. To keep them the same way as before, we can overwrite the slug for the specific post with one single line:

title: "How to Create a Dev Container for SQL Server 2019"
slug: "dev-container-for-sql-server-2019"

The next time we run the preview or generate the site, we get the old name for our post.

While migrating to MkDocs: figure out what slugs changed

The trick from above only works when we know what pages now have a different slug. We can build the site on our computer and then use the sitemap to check if the posts still have the same name – if we do it while the old blog software still runs:

import xml.etree.ElementTree as ET
import re
import requests
import time

def extract_slug(url):
    pattern = r'/([^/]+)/$'

    match = re.search(pattern, url)

    if match:
        return match.group(1)
    return None


def find_posts_in_sitempa(sitemap):
    posts = {}

    root = ET.parse(sitemap)

    namespace = {'ns': 'http://www.sitemaps.org/schemas/sitemap/0.9'}

    for loc in root.findall('ns:url/ns:loc', namespace):
        if "category" in loc.text:
            continue
        if "archive" in loc.text:
            continue
        if "page" in loc.text:
            continue
        if "tags" in loc.text:
            continue
        if "2" not in loc.text:
            continue

        slug = extract_slug(loc.text)

        posts[slug] = loc.text

    print(f"{len(posts)} posts found")
    return posts


if __name__ == "__main__":
    sitemap = r"..\site\sitemap.xml"

    print(f"Sitemap {sitemap}")
    valid_posts = find_posts_in_sitempa(sitemap)

    print(f"found URLs in sitemap: {len(valid_posts)}")

    errors = []
    for post in valid_posts:
        time.sleep(1)
        link = valid_posts[post]
        r = requests.get(link)
        print(f"{r.status_code} - {post} @ {link}")
        if r.status_code != 200:
            errors.append(post)

    print("#" * 50)
    print("Slugs to fix:")
    for post in errors:
        print(f"{post} -> {valid_posts[post]}")

We put this script into the scripts folder at the same level we have the mkdocs.yml file. The script runs through all our posts and checks if there is a post with the same URL on the old blog. At the end we get a list of posts where we need to change the slug.

If you do not have Cloudflare as a CDN you may speed things up and remove the call to time.sleep().

Keep the packages up to date

Material for MkDocs gets multiple releases per month. It is therefore important to keep the packages up to date. We best create a requirements.txt file with our packages:

1
2
3
mkdocs-material
mkdocs-material-extensions
mkdocs-rss-plugin

We can now run this command to update our packages in one go:

pip install -r requirements.txt --upgrade

Next

With the documentation and scripts from this post we get the tools to reduce a lot of the friction we may feel when we start with MkDocs. I hope these resources help you as much as they helped me. Next week we explore our options when it comes to speed up pip.