mkdocs-material/docs/plugins/blog.md

41 KiB
Raw Permalink Blame History

title icon
Built-in blog plugin material/newspaper-variant-outline

Built-in blog plugin

The blog plugin makes it very easy to build a blog, either as a sidecar to your documentation or as the main thing. Focus on your content while the plugin does all the heavy lifting, generating a view of all latest posts, archive and category pages, configurable pagination and much more.

Objective

How it works

The plugin scans the configured [posts directory][config.post_dir] for .md files from which paginated views1 are automatically generated. If not configured otherwise, the plugin expects that your project has the following directory layout, and will create any missing directories or files for you:

.
├─ docs/
│  └─ blog/
│     ├─ posts/
│     └─ index.md
└─ mkdocs.yml

The index.md file in the [blog directory][config.blog_dir] is the entry point to your blog a paginated view listing all posts in reverse chronological order. Besides that, the plugin supports automatically creating archive and category pages that list a subset of posts for a time interval or category.

[Post URLs][config.post_url_format] are completely configurable, no matter if you want your URLs to include the post's date or not. Rendered dates always display in the locale of the site language of your project. Like in other static blog frameworks, posts can be annotated with a variety of metadata, allowing for easy integration with other built-in plugins, e.g., the social and tags plugin.

Posts can be organized in nested folders with a directory layout that suits your specific needs, and can make use of all components and syntax that Material for MkDocs offers, including admonitions, annotations, code blocks, content tabs, diagrams, icons, math, and more.

When to use it

If you want to add a blog to your project, or migrate from another blog framework to Material for MkDocs because of its excellent technical writing capabilities, this plugin is a great choice, as it integrates perfectly with many other built-in plugins:

  • :material-file-tree:   Built-in meta plugin


    The meta plugin makes it easy to apply metadata to a subset of posts, including authors, tags, categories, draft status, as well as social card layouts.


    Simpler organization, categorization and management of post metadata

  • :material-share-circle:   Built-in social plugin


    The social plugin automatically generates beautiful and customizable social cards for each post and page, showing as previews on social media.


    Links to your blog render beautiful social cards when shared on social media

  • :material-rabbit:   Built-in optimize plugin


    The optimize plugin automatically identifies and optimizes all media files that you reference in your project by using compression and conversion techniques.


    Your blog loads faster as smaller images are served to your users

  • :material-tag-text:   Built-in tags plugin


    The tags plugin allows to categorize posts alongside with pages in your project, to improve their discoverability and connect posts to your documentation.


    Your documentation's tag system integrates with your blog

Configuration

As with all built-in plugins, getting started with the blog plugin is straightforward. Just add the following lines to mkdocs.yml, and you can start writing your first post:

plugins:
  - blog

The blog plugin is built into Material for MkDocs and doesn't need to be installed.

Navigation

If you do not have site navigation configured in your mkdocs.yml then there is nothing more to do. The blog archive and category pages will automatically appear underneath the automatically generated navigation.

If you do have a navigation structure defined then you will need to specify where the blog should appear in this. Create a navigation section with an index page for the blog:

theme:
  name: material
  features:
    - navigation.indexes
nav:
  - ...
  - Blog:
    - blog/index.md

The archive and category pages will appear within that section as subsections beneath pages in the blog section. In this case, they would appear after index.md. The path to the index.md file must match [blog_dir][config.blog_dir]. This means that you can name the blog navigation entry anything you like: 'Blog' or 'News' or perhaps 'Tips'.

General

The following settings are available:


Use this setting to enable or disable the plugin when building your project. It's normally not necessary to specify this setting, but if you want to disable the plugin, use:

plugins:
  - blog:
      enabled: false

Use this setting to change the path where your blog is located in the [docs directory][mkdocs.docs_dir]. The path is included in the generated URLs as a prefix for all posts and views. You can change it with:

=== "Documentation + Blog"

``` yaml
plugins:
  - blog:
      blog_dir: blog
```

=== "Blog only"

``` yaml
plugins:
  - blog:
      blog_dir: .
```

The provided path is resolved from the [docs directory][mkdocs.docs_dir].


Use this setting to leverage the table of contents to display post titles in views. This might be useful, if your post excerpts are rather long. If you want to enable it, use:

plugins:
  - blog:
      blog_toc: true

Posts

The following settings are available for posts:


Use this setting to change the folder where your posts are located. It's normally not necessary to change this setting, but if you want to rename the folder or change its file system location, use:

plugins:
  - blog:
      post_dir: "{blog}/articles"

Note that the [posts directory][config.post_dir] is solely used for post organization it is not included in post URLs, since they are automatically and comfortably generated by this plugin.

The following placeholders are available:

  • blog [blog directory][config.blog_dir]

The provided path is resolved from the [docs directory][mkdocs.docs_dir].


Use this setting to change the date format of posts. This plugin uses babel to render dates in the configured site language. You can use babel's pattern syntax or the following shortcodes:

=== "Monday, January 31, 2024"

``` yaml
plugins:
  - blog:
      post_date_format: full
```

=== "January 31, 2024"

``` yaml
plugins:
  - blog:
      post_date_format: long
```

=== "Jan 31, 2024"

``` yaml
plugins:
  - blog:
      post_date_format: medium
```

=== "1/31/24"

``` yaml
plugins:
  - blog:
      post_date_format: short
```

Note that depending on the site language, results might look different for other languages.


Use this setting to change the date format used in post URLs. The format string must adhere to babel's pattern syntax and should not contain whitespace. Some popular choices:

=== ":material-link: blog/2024/01/31/:material-dots-horizontal:/"

``` yaml
plugins:
  - blog:
      post_url_date_format: yyyy/MM/dd
```

=== ":material-link: blog/2024/01/:material-dots-horizontal:/"

``` yaml
plugins:
  - blog:
      post_url_date_format: yyyy/MM
```

=== ":material-link: blog/2024/:material-dots-horizontal:/"

``` yaml
plugins:
  - blog:
      post_url_date_format: yyyy
```

If you want to remove the date from post URLs, e.g., when your blog features mostly evergreen content, you can remove the date placeholder from the [post_url_format][config.post_url_format] format string.


Use this setting to change the format string that is used when generating post URLs. You can freely combine placeholders, and join them with slashes or other characters:

=== ":material-link: blog/2024/:material-dots-horizontal:/"

``` yaml
plugins:
  - blog:
      post_url_format: "{date}/{slug}"
```

=== ":material-link: blog/:material-dots-horizontal:/"

``` yaml
plugins:
  - blog:
      post_url_format: "{slug}"
```

The following placeholders are available:

  • categories Post categories, slugified with [categories_slugify][config.categories_slugify]
  • date Post date, formatted with [post_url_date_format][config.post_url_date_format]
  • slug Post title, slugified with [post_slugify][config.post_slugify], or explicitly set via [slug][meta.slug] metadata property
  • file Post filename without .md file extension

If you remove the date placeholder, make sure that post URLs don't collide with URLs of other pages hosted under the [blog directory][config.blog_dir], as this leads to undefined behavior.


Use this setting to set an upper bound for the number of categories included in post URLs if the categories placeholder is part of [post_url_format] [config.post_url_format] and the post defines categories:

plugins:
  - blog:
      post_url_format: "{categories}/{slug}"
      post_url_max_categories: 2

If more than one category is given, they are joined with / after slugifying.


Use this setting to change the function for generating URL-compatible slugs from post titles. By default, the slugify function from Python Markdown Extensions is used as follows:

plugins:
  - blog:
      post_slugify: !!python/object/apply:pymdownx.slugs.slugify
        kwds:
          case: lower

The default configuration is Unicode-aware and should produce good slugs for all languages. Of course, you can also provide a custom slugification function for more granular control.


Use this setting to change the separator that is passed to the slugification function set as part of [post_slugify][config.post_slugify]. While the default is a hyphen, it can be set to any string, e.g., _:

plugins:
  - blog:
      post_slugify_separator: _

By default, the plugin makes post excerpts optional. When a post doesn't define an excerpt, views include the entire post. This setting can be used to make post excerpts required:

=== "Optional"

``` yaml
plugins:
  - blog:
      post_excerpt: optional
```

=== "Required"

``` yaml
plugins:
  - blog:
      post_excerpt: required
```

When post excerpts are required, posts without excerpt separators raise an error. Thus, this setting is useful when you want to make sure that all posts have excerpts defined.


Use this setting to set an upper bound for the number of authors rendered in post excerpts. While each post may be written by multiple authors, this setting allows to limit the display to just a few or even a single author, or disable authors in post excerpts:

=== "Render up to 2 authors"

``` yaml
plugins:
  - blog:
      post_excerpt_max_authors: 2
```

=== "Disable authors"

``` yaml
plugins:
  - blog:
      post_excerpt_max_authors: 0
```

This only applies to post excerpts in views. Posts always render all authors.


Use this setting to set an upper bound for the number of categories rendered in post excerpts. While each post may be assigned to multiple categories, this setting allows to limit the display to just a few or even a single category, or disable categories in post excerpts:

=== "Render up to 2 categories"

``` yaml
plugins:
  - blog:
      post_excerpt_max_categories: 2
```

=== "Disable categories"

``` yaml
plugins:
  - blog:
      post_excerpt_max_categories: 0
```

This only applies to post excerpts in views. Posts always render all categories.


Use this setting to set the separator the plugin will look for in a post's content when generating post excerpts. All content before the separator is considered to be part of the excerpt:

plugins:
  - blog:
      post_excerpt_separator: <!-- more -->

It is common practice to use an HTML comment as a separator.


Use this setting to control whether the plugin should automatically compute the reading time of a post, which is then rendered in post excerpts, as well as in posts themselves:

plugins:
  - blog:
      post_readtime: false

Use this setting to change the number of words that a reader is expected to read per minute when computing the reading time of a post. If you want to fine-tune it, use:

plugins:
  - blog:
      post_readtime_words_per_minute: 300

A reading time of 265 words per minute is considered to be the average reading time of an adult.

Archive

The following settings are available for archive pages:


Use this setting to enable or disable archive pages. An archive page shows all posts for a specific interval (e.g. year, month, etc.) in reverse order. If you want to disable archive pages, use:

plugins:
  - blog:
      archive: false

Use this setting to change the title of the archive section the plugin adds to the navigation. If this setting is omitted, it's sourced from the translations. If you want to change it, use:

plugins:
  - blog:
      archive_name: Archive

Use this setting to change the date format used for archive page titles. The format string must adhere to babel's pattern syntax. Some popular choices:

=== "2024"

``` yaml
plugins:
  - blog:
      archive_date_format: yyyy
```

=== "January 2024"

``` yaml
plugins:
  - blog:
      archive_date_format: MMMM yyyy
```

Note that depending on the site language, results might look different for other languages.


Use this setting to change the date format used for archive page URLs. The format string must adhere to babel's pattern syntax and should not contain whitespace. Some popular choices:

=== ":material-link: blog/archive/2024/"

``` yaml
plugins:
  - blog:
      archive_url_date_format: yyyy
```

=== ":material-link: blog/archive/2024/01/"

``` yaml
plugins:
  - blog:
      archive_url_date_format: yyyy/MM
```

Use this setting to change the format string that is used when generating archive page URLs. You can freely combine placeholders, and join them with slashes or other characters:

=== ":material-link: blog/archive/2024/"

``` yaml
plugins:
  - blog:
      archive_url_format: "archive/{date}"
```

=== ":material-link: blog/2024/"

``` yaml
plugins:
  - blog:
      archive_url_format: "{date}"
```

The following placeholders are available:

  • date Archive date, formatted with [archive_url_date_format][config.archive_url_date_format]

Use this setting to enable or disable pagination for archive pages. The value of this setting is inherited from [pagination][config.pagination], unless it's explicitly set. To disable pagination, use:

plugins:
  - blog:
      archive_pagination: false

Use this setting to change the number of posts rendered per archive page. The value of this setting is inherited from [pagination_per_page] [config.pagination_per_page], unless it's explicitly set. To change it, use:

plugins:
  - blog:
      archive_pagination_per_page: 5

Use this setting to leverage the table of contents to display post titles on all archive pages. The value of this setting is inherited from [blog_toc] [config.blog_toc], unless it's explicitly set. To change it, use

plugins:
  - blog:
      archive_toc: true

Categories

The following settings are available for category pages:


Use this setting to enable or disable category pages. A category page shows all posts for a specific category in reverse chronological order. If you want to disable category pages, use:

plugins:
  - blog:
      categories: false

Use this setting to change the title of the category section the plugin adds to the navigation. If this setting is omitted, it's sourced from the translations. If you want to change it, use:

plugins:
  - blog:
      categories_name: Categories

Use this setting to change the format string that is used when generating category page URLs. You can freely combine placeholders, and join them with slashes or other characters:

=== ":material-link: blog/category/:material-dots-horizontal:/"

``` yaml
plugins:
  - blog:
      categories_url_format: "category/{slug}"
```

=== ":material-link: blog/:material-dots-horizontal:/"

``` yaml
plugins:
  - blog:
      categories_url_format: "{slug}"
```

The following placeholders are available:

  • slug Category, slugified with [categories_slugify][config.categories_slugify]

Use this setting to change the function for generating URL-compatible slugs from categories. By default, the slugify function from Python Markdown Extensions is used as follows:

plugins:
  - blog:
      post_slugify: !!python/object/apply:pymdownx.slugs.slugify
        kwds:
          case: lower

The default configuration is Unicode-aware and should produce good slugs for all languages. Of course, you can also provide a custom slugification function for more granular control.


Use this setting to change the separator that is passed to the slugification function set as part of [categories_slugify][config.categories_slugify]. While the default is a hyphen, it can be set to any string, e.g., _:

plugins:
  - blog:
      categories_slugify_separator: _

Use this setting to specify a custom function for sorting categories. For example, if you want to sort categories by the number of posts they contain, use the following configuration:

plugins:
  - blog:
      categories_sort_by: !!python/name:material.plugins.blog.view_post_count

Don't forget to enable [categories_sort_reverse][config.categories_sort_reverse]. You can define your own comparison function, which must return something that can be compared while sorting, i.e., a string or number.


Use this setting to reverse the order in which categories are sorted. By default, categories are sorted in ascending order, but you can reverse ordering as follows:

plugins:
  - blog:
      categories_sort_reverse: true

The plugin allows to check categories against a predefined list, in order to catch typos or make sure that categories are not arbitrarily added. Specify the categories you want to allow with:

plugins:
  - blog:
      categories_allowed:
        - Search
        - Performance

The plugin stops the build if a post references a category that is not part of this list. Posts can be assigned to categories by using the [categories] [meta.categories] metadata property.


Use this setting to enable or disable pagination for category pages. The value of this setting is inherited from [pagination][config.pagination], unless it's explicitly set. To disable pagination, use:

plugins:
  - blog:
      categories_pagination: false

Use this setting to change the number of posts rendered per category page. The value of this setting is inherited from [pagination_per_page] [config.pagination_per_page], unless it's explicitly set. To change it, use:

plugins:
  - blog:
      categories_pagination_per_page: 5

Use this setting to leverage the table of contents to display post titles on all category pages. The value of this setting is inherited from [blog_toc] [config.blog_toc], unless it's explicitly set. To change it, use:

plugins:
  - blog:
      categories_toc: true

Authors

The following settings are available for authors:


Use this setting to enable or disable post authors. If this setting is enabled, the plugin will look for a file named [.authors.yml][config.authors_file] and render authors in posts and views. Disable this behavior with:

plugins:
  - blog:
      authors: false

Use this setting to change the path of the file where the author information for your posts resides. It's normally not necessary to change this setting, but if you need to, use:

plugins:
  - blog:
      authors_file: "{blog}/.authors.yml"

The following placeholders are available:

  • blog [blog directory][config.blog_dir]

The provided path is resolved from the [docs directory][mkdocs.docs_dir].

!!! info "Format of author information"

The `.authors.yml` file must adhere to the following format:

``` yaml title=".authors.yml"
authors:
  <author>:
    name: string        # Author name
    description: string # Author description
    avatar: url         # Author avatar
    slug: url           # Author profile slug
    url: url            # Author website URL
```

Note that `<author>` must be set to an identifier for associating authors
with posts, e.g., a GitHub username like `squidfunk`. This identifier can
then be used in the [`authors`][meta.authors] metadata property of
a post. Multiple authors are supported. As an example, see
[the `.authors.yml` file][.authors.yml] we're using for our blog.

Use this setting to enable or disable automatically generated author profiles. An author profile shows all posts by an author in reverse chronological order. You can enable author profiles with:

plugins:
  - blog:
      authors_profiles: true

Use this setting to change the title of the authors section the plugin adds to the navigation. If this setting is omitted, it's sourced from the translations. If you want to change it, use:

plugins:
  - blog:
      authors_profiles_name: Authors

Use this setting to change the format string that is used when generating author profile URLs. You can freely combine placeholders, and join them with slashes or other characters:

=== ":material-link: blog/author/:material-dots-horizontal:/"

``` yaml
plugins:
  - blog:
      authors_profiles_url_format: "author/{slug}"
```

=== ":material-link: blog/:material-dots-horizontal:/"

``` yaml
plugins:
  - blog:
      authors_profiles_url_format: "{slug}"
```

The following placeholders are available:

  • slug Author slug or identifier from [authors_file][config.authors_file]
  • name Author name from [authors_file][config.authors_file]

Use this setting to enable or disable pagination for author profiles. The value of this setting is inherited from [pagination][config.pagination], unless it's explicitly set. To disable pagination, use:

plugins:
  - blog:
      authors_profiles_pagination: false

Use this setting to change the number of posts rendered per archive page. The value of this setting is inherited from [pagination_per_page] [config.pagination_per_page], unless it's explicitly set. To change it, use:

plugins:
  - blog:
      authors_profiles_pagination_per_page: 5

Use this setting to leverage the table of contents to display post titles on all author profiles. The value of this setting is inherited from [blog_toc] [config.blog_toc], unless it's explicitly set. To change it, use:

plugins:
  - blog:
      authors_profiles_toc: true

Pagination

The following settings are available for pagination:


Use this setting to enable or disable pagination in views generated pages that show posts or subsets of posts in reverse chronological order. If you want to disable pagination, use:

plugins:
  - blog:
      pagination: false

Use this setting to change the number of posts rendered per page. If you have rather long post excerpts, it can be a good idea to reduce the number of posts per page:

plugins:
  - blog:
      pagination_per_page: 5

Use this setting to change the format string that is used when generating paginated view URLs. You can freely combine placeholders, and join them with slashes or other characters:

=== ":material-link: blog/page/n/"

``` yaml
plugins:
  - blog:
      pagination_url_format: "page/{page}"
```

=== ":material-link: blog/n/"

``` yaml
plugins:
  - blog:
      pagination_url_format: "{page}"
```

The following placeholders are available:

  • page Page number

The plugin uses the paginate module to generate the pagination markup using a special syntax. Use this setting to customize how pagination is constructed. Some popular choices:

=== "1 2 3 .. n"

``` yaml
plugins:
  - blog:
      pagination_format: "~2~"
```

=== "1 2 3 .. n :material-chevron-right: :material-chevron-double-right:"

``` yaml
plugins:
  - blog:
      pagination_format: "$link_first $link_previous ~2~ $link_next $link_last"
```

=== "1 :material-chevron-right:"

``` yaml
plugins:
  - blog:
      pagination_format: "$link_previous $page $link_next"
```

The following placeholders are supported by paginate:

  • #!css $first_page Number of first reachable page

  • #!css $last_page Number of last reachable page

  • #!css $page Number of currently selected page

  • #!css $page_count Number of reachable pages

  • #!css $items_per_page Maximal number of items per page

  • #!css $first_item Index of first item on the current page

  • #!css $last_item Index of last item on the current page

  • #!css $item_count Total number of items

  • #!css $link_first Link to first page (unless on first page)

  • #!css $link_last Link to last page (unless on last page)

  • #!css $link_previous Link to previous page (unless on first page)

  • #!css $link_next Link to next page (unless on last page)


Use this setting to control whether pagination should be automatically disabled when the view only consists of a single page. If you want to always render pagination, use:

plugins:
  - blog:
      pagination_if_single_page: true

Use this setting to enable or disable persistence of content, i.e., if paginated views should also display the content of their containing view. If you want to enable this behavior, use:

plugins:
  - blog:
      pagination_keep_content: true

Drafts

The following settings are available for drafts:


Rendering [draft posts][meta.draft] can be useful in deploy previews. Use this setting to specify whether the plugin should include posts marked as drafts when building your project:

=== "Render drafts"

``` yaml
plugins:
  - blog:
      draft: true
```

=== "Don't render drafts"

``` yaml
plugins:
  - blog:
      draft: false
```

Use this setting to control whether the plugin should include posts marked as drafts when previewing your site. If you don't wish to include draft posts when previewing, use:

plugins:
  - blog:
      draft_on_serve: false

The plugin can automatically mark posts with future dates as drafts. When the date is past today, the post is automatically included when building your project, unless explicitly marked as draft:

plugins:
  - blog:
      draft_if_future_date: true

Usage

Metadata

Posts can define a handful of metadata properties that specify how the plugin renders them, in which views they are integrated, and how they are linked to each other. The metadata of each post is validated against a schema to allow for a quicker discovery of syntax errors.

The following properties are available:


Use this property to associate a post with authors by providing a list of identifiers as defined in the [authors_file][config.authors_file]. If an author can't be resolved, the plugin will terminate with an error:

---
authors:
  - squidfunk # (1)!
---

# Post title
...
  1. Authors are linked by using their identifiers. As an example, see the .authors.yml file we're using for our blog.

Use this property to associate a post with one or more categories, making the post a part of the generated category page. Categories are defined as a list of strings (whitespaces are allowed):

---
categories:
  - Search
  - Performance
---

# Post title
...

If you want to prevent accidental typos assigning categories to posts, you can set a predefined list of allowed categories in mkdocs.yml by using the [categories_allowed][config.categories_allowed] setting.


Use this property to specify a post's date. Note that this property is required, which means the build fails when it's not set. Additional dates can be set by using a slightly different syntax:

=== "Date"

``` yaml
---
date: 2024-01-31
---

# Post title
...
```

=== "Update date"

``` yaml
---
date:
  created: 2024-01-31 # (1)!
  updated: 2024-02-01
---

# Post title
...
```

1.  Each post must have a creation date set.

=== "Custom date"

``` yaml
---
date:
  created: 2024-01-31
  my_custom_date: 2024-02-01 # (1)!
---

# Post title
...
```

1.  The blog plugin validates all dates and allows to format them with
    [babel]'s [pattern syntax] in templates. When using theme extension,
    authors can add custom dates to templates.

    This was first requested in #5733.

The following date formats are supported:

  • 2024-01-31
  • 2024-01-31T12:00:00

Use this property to mark a post as draft. The plugin allows to include or exclude posts marked as drafts when building your project using the [draft][config.draft] setting. Mark a post as draft with:

---
draft: true
---

# Post title
...

Use this property to define a list of links that are rendered in the sidebar of a post. The property follows the same syntax as [nav][mkdocs.nav] in mkdocs.yml, supporting sections and even anchors:

=== "Links"

``` yaml
---
links:
  - setup/setting-up-site-search.md
  - insiders/index.md
---

# Post title
...
```

=== "Links with sections"

``` yaml
---
links:
  - setup/setting-up-site-search.md
  - Insiders:
    - insiders/index.md
    - insiders/getting-started.md
---

# Post title
...
```

=== "Links with anchors"

``` yaml
---
links:
  - plugins/search.md # (1)!
  - Insiders:
    - insiders/index.md#how-to-become-a-sponsor
    - insiders/getting-started.md#requirements
---

# Post title
...
```

1.  If a link defines an anchor, the plugin resolves the anchor from the
    linked page and sets the anchor title as a [subtitle].

All relative links are resolved from the [docs directory][mkdocs.docs_dir].


Use this property to explicitly set the reading time of a post in minutes. When [post_readtime][config.post_readtime] is enabled, the plugin computes the reading time of a post, which can be overridden with:

---
readtime: 15
---

# Post title
...

Use this property to explicitly set the slug of a post. By default, the slug of a post is automatically computed by the [post_slugify][config.post_slugify] function from the post's title, which can be overridden with:

---
slug: help-im-trapped-in-a-universe-factory
---

# Post title
...

Slugs are passed to [post_url_format][config.post_url_format].


!!! question "Missing something?"

When setting up your blog or migrating from another blog framework, you
might discover that you're missing specific functionality  we're happy to
consider adding it to the plugin! You can [open a discussion] to
ask a question, or create a [change request] on our [issue tracker], so we
can find out if it might be a good fit for the plugin.

  1. Views are pages that are automatically generated, i.e., the entry point to your blog listing all latest posts, as well as archive and category pages that list all posts associated with them through metadata in chronological order. ↩︎