Skip to content

Writing a Post

Posts are the heart of a BlogMore blog. Each post is a Markdown file with a small block of metadata at the top called frontmatter. This page covers everything you need to know to write, format, and publish a post.

Your first post

Create a new .md file inside your content directory. The filename becomes part of the post's URL, so choose something descriptive and lowercase:

posts/hello-world.md

At its simplest, a post looks like this:

---
title: Hello World
date: 2024-01-15
---

Welcome to my new blog! This is my first post.

That's all you need to get started. BlogMore will generate a full HTML page from this file, including the page title, date, and all the surrounding navigation.

Frontmatter

Frontmatter is the block of YAML at the very top of your Markdown file, enclosed between two lines of ---. It tells BlogMore everything it needs to know about the post.

Required fields

title

The title of the post. This appears as the page heading, in the browser tab, and in listings, feeds, and search results.

title: My Thoughts on Python 3.12

Optional fields

date

The publication date of the post. Posts are sorted chronologically by this field, so it's well worth setting. Posts without a date appear after all dated posts.

date: 2024-01-15

See Date formats below for all accepted formats.

tags

A list of tags for the post. Tags are specific topics that can span across categories. Each tag gets its own page on the site listing all posts with that tag.

tags: [python, tutorial, beginner]

Tags can be written in several ways — see Tags formats below.

category

A single broad category for the post. Each category gets its own page listing all posts in it. A post can belong to at most one category.

category: python

See Categories and tags below for guidance on when to use each.

author

The name of the post's author. If not set, BlogMore falls back to the default_author set in your configuration.

Example:

author: Dave Pearson

author_url

The URL of the post's author. If not set, BlogMore falls back to the default_author_url set in your configuration.

Example:

author_url: https://davep.org/

show_author

Whether to show the author's name on this post. This property always overrides the global show_author setting in your configuration. If an author is set (either here or via default_author), the name is displayed below the date/time as "By {author name}".

Example:

show_author: true

draft

Set to true to mark the post as a draft. Draft posts are excluded from the build by default. This is useful for work in progress that you're not ready to publish.

draft: true

To include drafts during local development, pass --include-drafts on the command line or set include_drafts: true in your configuration. See Building and Publishing for more.

description

A short description of the post. Used for the <meta name="description"> tag, Open Graph tags, and Twitter Card tags. If not set, BlogMore falls back to the first paragraph of the post's content.

description: A gentle introduction to Python decorators with practical examples.

cover

A URL or path to a cover image, used for Open Graph, Twitter Card, and JSON-LD structured data. If not set, BlogMore falls back to the generated platform icons (if available) or the site_logo.

cover: /images/my-post-cover.png

twitter_creator

The Twitter/X handle of the post's author. Used in Twitter Card meta tags.

twitter_creator: "@davep"

twitter_site

The Twitter/X handle of the site. Used in Twitter Card meta tags.

twitter_site: "@my_blog"

modified

The date the post was last modified. Used in the <meta name="last-modified"> tag and displayed after the publication date on the post page and in all post listing summaries. Accepts the same formats as date.

When present, the modified date is shown in parentheses and italics after the publication date:

2026-04-27 08:40:40 UTC+01:00 (Modified: 2026-04-28 09:40:40 UTC+01:00)
modified: 2024-06-01

invite_comments

Override the global invite_comments setting for this post. Set to true to show the comment invitation section on this post even if it is disabled globally, or to false to hide it on this post even if it is enabled globally.

invite_comments: false

invite_comments_to

Override the email address used in the comment invitation link for this post. When set, this value is used as a literal email address — no template expansion is applied. This takes precedence over the global invite_comments_to setting.

invite_comments_to: "specific-address@example.com"

This key only has an effect when the comment invitation feature is enabled (either globally via invite_comments or via the per-post invite_comments front-matter key above).

show_toc

Whether to show the Table of Contents for this post. If not specified, this defaults to the global show_toc setting in your configuration (which itself defaults to true). Set to false to hide the Table of Contents on this specific post even if it contains headings, or to true to show it even if disabled globally.

show_toc: false

show_toc_inline

Whether to show the inline/collapsed Table of Contents on narrow screens for this post. If not specified, this defaults to the global show_toc_inline setting in your configuration (which itself defaults to true). Set to false to hide the inline Table of Contents on narrow displays for this specific post (while still showing the floating sidebar Table of Contents on wider displays, provided show_toc is enabled).

show_toc_inline: false

redirect_from

A list of URL paths (or a single URL path) that should redirect to this post. Fallback HTML redirection files containing a <meta http-equiv="refresh" content="0; url=..."> redirect and a canonical link tag will be written at these alias paths during the build process.

If the redirect path ends with a slash or has no extension, it will be treated as a directory and write an index.html file inside it. If it has a file extension (e.g. .html or .htm), it will write that file directly.

Examples:

# Redirect from legacy URLs
redirect_from:
  - /2021/05/my-post/
  - /old-category/my-post.html

Or a single path:

redirect_from: /old-post-slug

Date formats

BlogMore accepts dates in several formats:

date: 2024-01-15              # Simple date
date: 2024-01-15 14:30:00     # Date with time
date: 2024-01-15T14:30:00Z    # ISO 8601 with timezone

All formats work for both date and modified.

Tags formats

Tags can be written in three ways — choose whichever you find most readable:

# Inline YAML list (recommended)
tags: [python, web, tutorial]

# Comma-separated string
tags: python, web, tutorial

# Multi-line YAML list
tags:
  - python
  - web
  - tutorial

Categories and tags

Both categories and tags help visitors find related content, but they serve different purposes:

  • Category — the broad subject area the post belongs to. Think of it as the section of a magazine: python, devops, book-reviews. A post has at most one category.
  • Tags — specific topics within a post that might cut across categories. Think of them as an index: tutorial, beginner, type-hints, testing. A post can have as many tags as you like.

A well-organised post might look like this:

---
title: Python Decorators Explained
category: python
tags: [tutorial, intermediate, decorators]
---

Visitors can navigate to /category/python.html to see all posts in that category, or to /tag/tutorial.html to see all posts tagged with tutorial.

Series

If you publish multi-part articles, you can group them into a cohesive series. Series membership is defined using the series frontmatter key.

A post can belong to a single series, or to multiple series.

Series formats

Like tags, series can be written as a single string (for a single series) or as a list:

# A single series title
series: Designing a Static Site Generator

# Multiple series (as a YAML list)
series: [Designing a Static Site Generator, Web Development Basics]

# Multiple series (as a multi-line YAML list)
series:
  - Designing a Static Site Generator
  - Web Development Basics

Within each series, posts are automatically sorted in chronological order using their original posting date and time (the date frontmatter value; the modified date has no effect on sorting).

When generating post pages, BlogMore automatically adds a series banner at the top and bottom of each post belonging to a series. This banner displays a message (e.g. This post is part of the "Designing a Static Site Generator" series) and provides previous/next navigation links linking to other parts of the series.

Markdown features

BlogMore supports standard Markdown plus several extensions. The following sections cover the most useful ones.

Basic formatting

Standard Markdown formatting all works as expected:

**bold**, *italic*, `inline code`, ~~strikethrough~~

> Blockquote text

- unordered list
- second item

1. ordered list
2. second item

[link text](https://example.com)

![alt text](/path/to/image.png)

Strikethrough

Wrap text in double tildes (~~) to render it with a line through it:

This is ~~deleted~~ and this is ~~struck-out text~~ in a sentence.

This produces text where the marked words appear with a horizontal line through them, rendered as an HTML <del> element. Use it to indicate content that has been removed or is no longer relevant.

Code blocks with syntax highlighting

Use fenced code blocks with a language identifier for syntax highlighting:

```python
def greet(name: str) -> str:
    return f"Hello, {name}!"
```

A wide range of languages are supported, including python, javascript, typescript, bash, yaml, json, html, css, sql, rust, go, and many more.

Diagrams (Mermaid)

If enabled, BlogMore supports rendering diagrams and flowcharts written inside fenced code blocks using the mermaid language identifier:

```mermaid
graph TD
    A[Start] --> B[Process]
    B --> C{Decision}
    C -->|Yes| D[Success]
    C -->|No| E[Fail]
```

To use Mermaid diagrams:

  • Enable the feature: You must turn on Mermaid rendering globally by setting with_mermaid: true in your blogmore.yaml configuration file.
  • Efficient loading: To keep page load speeds fast, the Mermaid JS library is only loaded from a CDN on pages (posts, pages, or index/listings) that actually contain a diagram block.
  • More options: For full syntax details, layout options, and diagram types (such as sequence diagrams, pie charts, gantt charts, and user journeys), see the official Mermaid documentation.

LaTeX Math Rendering

If enabled, BlogMore supports rendering mathematical equations and formulas written in standard LaTeX notation:

  • Inline math: Enclose the formula between single dollar signs, like $e^{i\pi} + 1 = 0$ (note: there must be no space immediately inside the delimiters).
  • Block math: Enclose the formula between double dollar signs on separate lines:
    $$
    f(x) = \int_{-\infty}^{\infty} \hat{f}(\xi) e^{2 \pi i x \xi} d\xi
    $$
    

To use LaTeX math equations:

  • Enable the feature: You must turn on math rendering globally by setting with_maths: true in your blogmore.yaml configuration file.
  • Select a rendering engine: You can configure your preferred engine via maths_provider (defaults to 'katex' for instant rendering, or 'mathjax' for full feature support and accessibility).
  • Efficient loading: To keep page load speeds fast, the chosen rendering library is only loaded from a CDN on pages that actually contain mathematical equations.

Tables

| Feature       | Supported |
|---------------|-----------|
| Code blocks   | Yes       |
| Tables        | Yes       |
| Footnotes     | Yes       |
| Admonitions   | Yes       |

Footnotes

This is a statement that needs a citation.[^1]

[^1]: The source of the citation.

The footnote marker becomes a superscript link, and the footnote text is rendered at the bottom of the post.

Admonitions

BlogMore supports GitHub-style alert boxes, written as blockquotes with a special tag on the first line:

> [!NOTE]
> Useful information that readers should know.

> [!TIP]
> A helpful suggestion for doing things better.

> [!IMPORTANT]
> Key information that readers must not miss.

> [!WARNING]
> Urgent information that needs immediate attention.

> [!CAUTION]
> Advice about risks or negative outcomes.

Each type is rendered with a distinct colour scheme and icon so that readers can recognise them at a glance.

Every heading in a post automatically receives an id attribute derived from its text. For example:

## Getting Started

becomes:

<h2 id="getting-started">Getting Started</h2>

This means you can link directly to any section of a post by appending its fragment to the URL — for example https://example.com/2024/01/15/my-post.html#getting-started.

Custom heading IDs

If you need a specific id — for example because the auto-generated one is too long, or because you want a stable id that won't change if you reword the heading — you can set it explicitly by appending {#your-id} to the end of the heading line:

### My Great Heading {#custom-id}

This produces:

<h3 id="custom-id">My Great Heading</h3>

The custom id overrides the auto-generated one. Headings without a {#…} suffix keep their auto-generated IDs as usual.

To make it easy for readers to share a link to a specific section, BlogMore renders a small symbol at the end of every heading. The symbol is invisible by default and appears when the reader moves the mouse over the heading. Clicking the symbol navigates the browser to that heading's URL fragment, where the address can be copied from the browser's location bar.

The anchor appears and disappears with a smooth fade and does not affect the layout of the page in any way.

Table of Contents

For long posts with many sections, a table of contents makes it easy for readers to navigate your content. It provides a quick overview of the post's structure and lets users jump directly to any heading.

Syntax

To insert a table of contents, place [TOC] on its own line in your Markdown file:

# My Long Article

Here is an overview of what we will cover:

[TOC]

## Section One
...
## Section Two
...

How it works

BlogMore automatically scans your post for all headings (# through ######) and generates a Table of Contents.

  • Sidebar & Responsive TOC: By default, on desktop displays, a floating, sticky Table of Contents is shown in the right-hand margin of the page. On smaller mobile screens, this TOC is presented inline at the top of the post under a collapsible accordion.
  • Manual TOC: You can also insert the TOC manually at a specific place inside the post content by placing [TOC] on its own line.
  • Disabling the TOC: If you have headings but do not want a Table of Contents to be displayed at all (neither floating nor inline), you can disable it globally in your configuration or set the show_toc property to false in the post's frontmatter:
    show_toc: false
    
  • Disabling the inline TOC only: If you want to keep the floating sidebar TOC on wider screens but hide the inline/collapsed TOC on narrow displays, you can disable it globally in your configuration or set the show_toc_inline property to false in the post's frontmatter:
    show_toc_inline: false
    
  • The links target the automatically generated heading IDs (see Heading IDs and anchor links above).
  • The generated list is automatically nested based on heading levels (e.g., ### headings are nested under ## headings).
  • The table of contents is pre-styled to match your blog's theme, with hover effects and responsive indentation.

Markdown inside HTML tags

By default, Markdown inside raw HTML blocks is left as-is and is not processed further. If you want Markdown to be parsed inside an HTML element, add markdown="1" to the opening tag:

<div markdown="1">
*This text will be italic* and **this will be bold**.
</div>
<center markdown="1">
*This centered text will be italic* and **this will be bold**.
</center>

Without markdown="1", any Markdown inside the HTML tag is passed through verbatim and will not be rendered. With the attribute present, the content is treated as normal Markdown and all the usual formatting rules apply.