---
title: "Read YAML front matter as a table, not raw text"
slug: read-yaml-front-matter-as-a-table
excerpt: "Turn the YAML header of an agent-written Markdown file into a field and value table with yq or a short Python script, so you can scan it fast."
quickAnswer: "To read YAML front matter fast, turn it into a table: one row per field, nested fields flattened to a dotted name like review.due, and, in the Python table, lists shown as one comma list. The Go-based yq, version 4, prints each field as a key = value line and can read one field on its own. A short Python script with PyYAML prints a Markdown table you can paste into any file. Both tools stop with an error if the header is not valid YAML."
keyTakeaways:
  - "A table puts one field on each row, so status, owner and dates are easy to find at a glance."
  - "Flatten the header: a nested field becomes a dotted name such as review.due."
  - "The Go-based yq, version 4, prints front matter as a key = value list."
  - "A short Python script with PyYAML prints the header as a Markdown table and escapes pipes so the table still renders."
  - "Sashmark is in development and not released; its Frontmatter Table is planned for v1."
publishedDate: 2026-10-20
dateModified: 2026-10-20
author: Bobby Huang
cluster: E
role: post
status: published
faq:
  - q: Which yq do these commands need?
    a: The Go-based yq, version 4. Another tool with the same name is a Python wrapper around jq, and it takes different flags, so these commands will not work with it. Check which one you have before you copy the commands.
  - q: How do nested fields show up in the table?
    a: They are flattened to a dotted name. A review block with by and due inside it becomes two rows, review.by and review.due. That makes nested fields as easy to scan as top-level ones.
  - q: What happens if the YAML header is broken?
    a: Neither tool guesses. yq prints an error and exits with code 1, and the Python script stops with a YAML error. Fix the header first, then run the tool again.
related:
  - yaml-front-matter-in-markdown
  - front-matter-fields-status-date-owner
  - yaml-front-matter-errors
---

Agents like to start their Markdown files with a YAML header: a status, an owner, a couple of dates, some tags, and often a nested block for review. That header is the fastest way to know what a file is and whether it needs you. Raw YAML hides it well, though. Indentation decides what belongs to what, lists sit on one line or many, and the field you want can be anywhere in the block.

This page shows two quick ways to read YAML front matter as a table instead. The first uses yq to print each field as a key = value line. The second is a short Python script that prints a real Markdown table. Both run in a terminal.

If you need the basics first, start with [what front matter is](what-is-front-matter.md). This post assumes you already have files with a header and want to scan them faster.

## Why a table reads faster than raw YAML

A table gives each field its own row. The field name sits in the left column and the value sits in the right, so your eye runs straight down one edge to find status or owner.

Two small changes make the table easier still:

- **Nested fields get a dotted name.** A `due` field inside a `review` block becomes `review.due`. You see where it lives without counting spaces.
- **Lists become one comma list.** In the Python table below, tags written as `[ui, settings]` show as `ui, settings` in one cell.

That's the whole idea. The rules for how front matter is written, and which tools read it, are in [YAML front matter in Markdown](yaml-front-matter-in-markdown.md).

## The sample file

Both recipes below use this file, saved as `plan.md`:

```md
---
title: Settings page dark theme
status: in-progress
owner: Jane Doe
date: 2026-10-19
tags: [ui, settings]
review:
  by: Doe Holdings
  due: 2026-10-24
---

# Settings page dark theme

Body text.
```

## Recipe 1: read YAML front matter with yq

These commands are for the Go-based yq, version 4. Another tool is also called yq (a Python wrapper around jq), and it takes different flags. If a command below fails on its flags, check which yq you have.

1. Print only the header. This shows the YAML block (with its leading `---`) and nothing from the body:

   ```sh
   yq --front-matter=extract '.' plan.md
   ```

2. Print the header as a key = value list. The props output flattens everything for you:

   ```sh
   yq --front-matter=extract -o=props '.' plan.md
   ```

   Output:

   ```text
   title = Settings page dark theme
   status = in-progress
   owner = Jane Doe
   date = 2026-10-19
   tags.0 = ui
   tags.1 = settings
   review.by = Doe Holdings
   review.due = 2026-10-24
   ```

   Nested fields already have dotted names. Lists show one item per line, numbered from 0.

3. Read one field. Give yq the path to it:

   ```sh
   yq --front-matter=extract '.status' plan.md
   ```

   This prints `in-progress`. For a nested field, add the parent name:

   ```sh
   yq --front-matter=extract '.review.due' plan.md
   ```

   This prints `2026-10-24`.

The single-field form is handy when you only care about one thing, like whether a file is still in progress.

## Recipe 2: a short Python script that prints a table

If you want a real table, this script does it. It needs Python 3 and the PyYAML package. Save it as `fm_table.py`:

```python
import sys, yaml

lines = open(sys.argv[1], encoding="utf-8").read().splitlines()
if not lines or lines[0] != "---":
    sys.exit("no front matter")
end = lines.index("---", 1)
data = yaml.safe_load("\n".join(lines[1:end]))

def rows(d, prefix=""):
    for key, value in d.items():
        name = f"{prefix}{key}"
        if isinstance(value, dict):
            yield from rows(value, name + ".")
        elif isinstance(value, list):
            yield name, ", ".join(str(v) for v in value)
        else:
            yield name, str(value)

def cell(text):
    return text.strip().replace("\n", " ").replace("|", "\\|")

print("| Field | Value |")
print("| --- | --- |")
for name, value in rows(data):
    print(f"| {cell(name)} | {cell(value)} |")
```

Run it on the sample file:

```sh
python3 fm_table.py plan.md
```

Output:

```text
| Field | Value |
| --- | --- |
| title | Settings page dark theme |
| status | in-progress |
| owner | Jane Doe |
| date | 2026-10-19 |
| tags | ui, settings |
| review.by | Doe Holdings |
| review.due | 2026-10-24 |
```

Paste that output into a Markdown file and it renders as a table. If [Markdown tables](markdown-tables.md) are new to you, that post covers the syntax.

A few details worth knowing:

- **Cells stay on one line.** A pipe character would normally split a table cell, and a line break would end the row. The script turns `|` into `\|` and joins a value that spans several lines into one line, so a title like `A | B test` prints as `| title | A \| B test |` and the table still renders. Quoting values like that one in the YAML itself is covered in [YAML front matter quoting](yaml-front-matter-quoting.md).
- **The header has to come first.** The script only reads a header that starts on the first line with `---` and ends at the next line that is exactly `---`. A file without that first line gets the message "no front matter", and a header that is never closed stops the script with an error.
- **It expects key and value pairs.** The header should be a set of fields, not a bare list or a single value.

## When the header is broken

Sometimes the header itself is the problem. Take a title like `title: Settings: dark theme`, with an unquoted colon in the middle. Neither tool guesses at what was meant.

yq prints this and exits with code 1:

```text
Error: bad file 'bad.md': yaml: mapping values are not allowed in this context
```

The Python script stops with:

```text
yaml.scanner.ScannerError: mapping values are not allowed here
```

That's useful. A loud error is better than a table with a wrong value in it. For what each error means and how to fix it, see [YAML front matter errors](yaml-front-matter-errors.md).

## Habits that make the table pay off

A table only helps if you read it with a purpose. These habits keep it quick:

1. **Check status, owner and dates first.** They tell you whether the file needs you today. Which fields to include in the first place is covered in [front matter fields for status, date and owner](front-matter-fields-status-date-owner.md).
2. **Look for missing rows.** A field that isn't in the header simply has no row. If you expect an owner and there's no owner row, the agent left it out. That's easier to spot in a short table than in a block of YAML.
3. **Read the dotted rows.** Nested fields are the easiest to miss in raw YAML, because they hide under a parent at a deeper indent. In the table, `review.due` sits in the same column as everything else.
4. **Compare files with the same rows.** Run the script on two plans and the field names line up, so a different owner or a later due date stands out.

## Where Sashmark fits

Sashmark is a Markdown reader and editor for people whose AI agents write Markdown, being built for Mac, Windows and Linux. It is in development and has not been released, and the app itself has no AI features. A good reader should show the YAML header as a small key and value table above the document, with the raw YAML one step away for when you need to edit it. Sashmark is being built for this: its Frontmatter Table is planned for v1, along with the other [planned features](/features). The [help pages](/help) describe how it is meant to work, and the [roadmap](/roadmap) shows where it stands.

Sashmark is in development. See what it is at [sashmark.com](https://www.sashmark.com/).
