Markdown Frontmatter: YAML Metadata Explained (2026)

September 11, 2026 · 9 min read

Markdown Frontmatter: YAML Metadata Explained

A markdown frontmatter block is YAML between two --- lines at the very top of a .md file. It holds metadata such as the title, date, tags, and draft status, and a static site generator or notes app reads it before rendering the body. Markdown itself doesn't know it exists, which is why the same file can look wrong in a plain editor.

Markdown Front Matter Syntax

Front matter has three rules. It must be the first thing in the file, it must be valid YAML, and it must sit between a line containing only --- and another line containing only ---. Here is a typical block for a blog post:

---
title: "Markdown Frontmatter: YAML Metadata Explained"
date: 2026-09-11
tags: [markdown, yaml, metadata]
draft: true
---

Body text starts after the second line of dashes.

Inside the block, each line is a key: value pair. Strings can be bare or quoted, numbers and booleans are bare, and dates in YYYY-MM-DD form parse as dates. A generator such as Jekyll or Hugo strips the block, stores the values, and renders only the body. The Jekyll front matter docs state the rule plainly. The block "must be the first thing in the file" and must be valid YAML between triple-dashed lines.

Hugo accepts two more formats: TOML between +++ lines and JSON wrapped in braces. The Hugo front matter reference documents all three. In practice, YAML with --- is what nearly every tool expects, so use it unless your generator says otherwise.

What Happens to Frontmatter in a Plain Markdown Renderer?

Nothing good. The CommonMark spec has no concept of a metadata block, so a standard renderer treats the lines as ordinary Markdown. The first --- becomes a horizontal rule. The key-value lines become a paragraph. And because a --- line directly under paragraph text is setext heading syntax, the second --- turns that paragraph into a level-2 heading.

We tested this in our editor, which uses a CommonMark-style parser. The block above renders as a rule, then a single level-2 heading holding all four key-value lines, then the body. The Markdown horizontal line guide explains why three dashes behave that way.

GitHub is the exception. When a .md file in a repository starts with a YAML block, GitHub renders it as a two-column key and value table above the body. We confirmed this on Jekyll's own front-matter.md in the jekyll/jekyll repository, where the title, permalink, and redirect_from keys appear as a table at the top of the rendered file. Everywhere else, assume the block is visible unless the tool documents otherwise.

Paste the block below into any generator and it disappears. In the preview it shows as a rule and a heading instead. That is the "why is my metadata showing" bug people hit when they open a Jekyll post in a generic app.


title: Weekly notes
date: 2026-09-11
tags: [notes, weekly]
draft: false

What shipped

  • Export redesign
  • Faster preview
20 words133 characters11 lines
Markdown

Convert with the Markdown to HTML tool and you'll see the <hr> and <h2> in the output. If you need to hide metadata from a plain renderer, front matter isn't the tool; see Markdown comments for the options that actually hide text.

How Do You Write an Array in Markdown Frontmatter?

YAML gives you two ways to write a list, and both are valid. The flow form puts items in square brackets on one line. The block form puts each item on its own line after a dash.

---
tags: [markdown, yaml, metadata]
categories:
  - writing
  - tooling
---

Obsidian writes the block form when you add tags through its Properties panel. Jekyll and Hugo accept either form. Two things break arrays. A tab character in the indentation is invalid YAML, and a missing space after the dash turns -writing into a single string beginning with a hyphen.

YAML Rules That Break Markdown Front Matter

Most front matter bugs are YAML bugs. The YAML 1.2.2 spec is long, but five rules cover nearly every failure we've seen:

  • Colons in values need quotes. title: Note: read this fails, because YAML sees a second key. Write title: "Note: read this".
  • Hashes start comments. title: Issue #42 stores only Issue. Quote it.
  • Booleans are bare words. draft: true is a boolean; draft: "true" is a string, and some generators treat a non-empty string as true.
  • Dates are strings or timestamps depending on the tool. Hugo parses 2026-09-11 and 2026-09-11T13:18:50-07:00; when no offset is given it uses the site's timeZone setting, and UTC if that isn't set either. Jekyll also reads YYYY-MM-DD HH:MM:SS +/-TTTT.
  • Multi-line strings use | or >. A literal block (|) keeps line breaks; a folded block (>) joins lines with spaces. Use them for long descriptions.
---
title: "Release 2.0: what changed"
summary: >
  A folded block joins these lines
  into one paragraph.
draft: false
---

Indentation must be spaces, and two spaces per level is the convention. A single stray tab makes the whole block unparseable, and most generators then either fail the build or, worse, render the block as body text.

Front Matter Fields by Platform

Each tool reads its own keys and ignores the rest, so a file can carry fields for two systems at once. These are the fields the official docs list:

PlatformReserved or common keysNotes
Jekylllayout, permalink, published, date, categories, tagspublished: false hides a post; date overrides the filename date
Hugotitle, date, draft, weight, description, slug, lastmod, publishDate, expiryDate, paramsYAML, TOML, or JSON; custom values go under params
Obsidiantags, aliases, cssclassesCalled properties; typed as text, list, number, checkbox, date, date and time, or tags
Astro content collectionsWhatever your Zod schema definesdefineCollection with z.object(...) validates every file at build time and fails the build on a mismatch
GitHubAnyRendered as a table above the file; no keys are interpreted
Docusaurusid, title, sidebar_label, sidebar_position, slug, tagsControls sidebar label, order, and URL

Obsidian's Properties documentation notes that each name must be unique in a note, that internal links inside list properties need quotes, and that Markdown formatting is not rendered in text properties. The Obsidian Markdown cheat sheet covers the rest of Obsidian's syntax. Astro's content collections guide is worth reading even if you don't use Astro, because schema validation is the only approach here that catches a typo in a key name.

Reading Frontmatter Markdown in Code

Parsing is a solved problem in every language. In Node, gray-matter (4.0.3 on npm) returns the metadata as data and the body as content:

import fs from "node:fs";
import matter from "gray-matter";

const file = matter(fs.readFileSync("post.md", "utf8"));
console.log(file.data.title); // "Markdown Frontmatter: YAML Metadata Explained"
console.log(file.content);    // body without the block

In Python, python-frontmatter (1.3.0 on PyPI) does the same with frontmatter.load, and the metadata is available both as post.metadata and by key:

import frontmatter

post = frontmatter.load("post.md")
print(post["title"])
print(post.content)

If you already run a remark pipeline, add remark-frontmatter so the block is parsed as a yaml node instead of a rule and a heading. Webpack projects have the frontmatter-markdown-loader package for the same job. That is what most React and Astro Markdown setups do under the hood. Front matter also pairs naturally with the file skeletons in our Markdown templates post, where the blog-post template starts with a YAML block.

Common Markdown Frontmatter Mistakes

A blank line before the first ---. Jekyll and most parsers then treat the file as having no front matter, and the block renders as body text. Delete everything above the dashes, including the byte-order mark some Windows editors add.

Unquoted colons in titles. title: Markdown Frontmatter: YAML Metadata Explained fails YAML parsing. Wrap the whole value in double quotes.

Expecting the block to hide text. Front matter markdown is metadata, not a comment. A plain renderer shows it, and GitHub shows it as a table. To hide text from every renderer, use an HTML comment instead.

Markdown Frontmatter FAQ

Think of markdown frontmatter as a contract with one specific tool rather than a feature of the language. Keep it first in the file and keep it valid YAML with spaces rather than tabs. Quote any value containing a colon or hash, and check which keys your generator actually reads. Paste a block into the editor to see how a plain renderer treats it, then switch to your generator's preview to confirm the metadata is being picked up.