---
title: "Markdown numbered headings: pandoc section numbers tested"
slug: markdown-numbered-headings
excerpt: "Markdown numbered headings in one pandoc test; the section Number headings with pandoc's markdown reader gives the HTML pandoc wrote."
quickAnswer: "Under Heading numbers in the CommonMark spec, zero-count grep searches sit beside one control search. The section Number headings with pandoc's markdown reader quotes pandoc's HTML for a run with section numbering turned on. In Leave a heading out of the count, pandoc's output for two marked headings is quoted. A run with an offset of 2 is quoted in Start the count at a different number. Output from two more reader settings appears in Plain commonmark and the attributes extension. LaTeX lines from the same test file are quoted under Heading numbers in LaTeX output. Six steps with cat, pandoc and grep commands make up Check heading numbers with pandoc."
keyTakeaways:
  - "Spec grep counts and a control count appear under Heading numbers in the CommonMark spec."
  - "Pandoc's HTML with section numbering turned on is quoted in Number headings with pandoc's markdown reader."
  - "Output for two headings marked to skip the count sits in Leave a heading out of the count."
  - "Quoted output from one offset run is shown in Start the count at a different number."
  - "Results for two commonmark reader settings are compared in Plain commonmark and the attributes extension."
publishedDate: 2026-10-20
dateModified: 2026-10-20
author: Bobby Huang
cluster: B
role: post
status: published
faq:
  - q: Does CommonMark have numbered headings?
    a: "The grep results and control count for the spec file sit in Heading numbers in the CommonMark spec."
  - q: How do I number headings with pandoc?
    a: "Under Number headings with pandoc's markdown reader, the command and its HTML output are quoted."
  - q: Why did a heading marked to skip still get a number?
    a: "Pandoc output from two commonmark reader settings is quoted in Plain commonmark and the attributes extension."
  - q: Can I start numbering at a number other than 1?
    a: "The section Start the count at a different number shows pandoc output for one offset run."
related:
  - markdown-table-of-contents
  - markdown-headings-anchor-links
  - markdown-numbered-lists
---

You want headings that read 1, 1.1, 2 and 2.1, and you'd rather not type them. If you type them yourself, moving a section means fixing them by hand.

This post hands one small file of markdown numbered headings to pandoc and tries one reader setting per run. Pandoc reported version 3.8.3 for these runs, and the machine ran macOS. It quotes what pandoc printed for the numbering option, the offset option, two ways to mark a heading to skip, and LaTeX output. No viewer was part of this test; the results end with what pandoc printed.

## Heading numbers in the CommonMark spec

ATX heading rules sit in section 4.2 of CommonMark release 0.31.2. Its lines 1043 to 1053 describe an ATX heading: 1 to 6 `#` characters to open the line, followed by text parsed as inline content. That rule says nothing about numbers.

A grep pass over the spec file turned up no numbering markup in this test. Three searches each returned 0: `grep -c 'unnumbered'`, `grep -ci 'section number'` and `grep -c 'number-sections'`. The two brace markers came back empty as well, each searched as a fixed string with `grep -cF`. The control search, `grep -ci 'heading'`, matched 67 lines of that file.

The numbers in the runs below came from pandoc options.

## The test file

The test file `hnum.md` holds six headings. Running `cat hnum.md` printed:

```markdown
# Intro

## Setup

# Usage

## Notes {-}

## Extra {.unnumbered}

## Steps
```

Two of the level-2 headings, Notes and Extra, end with a skip marker in braces, and the two markers are written in different ways. The rest are plain headings.

## Number headings with pandoc's markdown reader

Here is the command, with section numbering turned on:

```sh
pandoc -f markdown -t html --number-sections hnum.md
```

Pandoc printed this, line breaks and all:

```html
<h1 data-number="1" id="intro"><span
class="header-section-number">1</span> Intro</h1>
<h2 data-number="1.1" id="setup"><span
class="header-section-number">1.1</span> Setup</h2>
<h1 data-number="2" id="usage"><span
class="header-section-number">2</span> Usage</h1>
<h2 class="unnumbered" id="notes">Notes</h2>
<h2 class="unnumbered" id="extra">Extra</h2>
<h2 data-number="2.1" id="steps"><span
class="header-section-number">2.1</span> Steps</h2>
```

Four headings got a `data-number` attribute and a `span` whose class is `header-section-number`. The two level-1 headings counted 1 and 2. The level-2 headings under them counted 1.1 and 2.1. The number went into the heading text, inside the span, ahead of the words.

Without the option, the same reader wrote no numbers. The first line of that run was `<h1 id="intro">Intro</h1>`. The `id` values were there in both runs; the [heading anchor links guide](markdown-headings-anchor-links.md) covers linking to them.

## Leave a heading out of the count

In the numbered run above, Notes and Extra came out as `<h2 class="unnumbered" id="notes">Notes</h2>` and `<h2 class="unnumbered" id="extra">Extra</h2>`. Neither one got a `data-number` or a span. The brace text was gone from both headings.

Steps, the heading after them, went on as 2.1. The two skipped headings did not use up a number.

The `markdown` reader treated the two skip markers alike in this test. Even the run without the numbering option wrote `class="unnumbered"` on those two headings.

A run with `header_attributes` turned off, `pandoc -f markdown-header_attributes -t html --number-sections hnum.md`, numbered all six headings. Notes became 2.1, Extra became 2.2 and Steps became 2.3. Pandoc kept both markers as heading text and gave those two headings new ids.

## Start the count at a different number

This run added the offset option, set to 2, to the numbered command:

```sh
pandoc -f markdown -t html --number-sections --number-offset=2 hnum.md
```

Its output:

```html
<h1 data-number="3" id="intro"><span
class="header-section-number">3</span> Intro</h1>
<h2 data-number="3.1" id="setup"><span
class="header-section-number">3.1</span> Setup</h2>
<h1 data-number="4" id="usage"><span
class="header-section-number">4</span> Usage</h1>
<h2 class="unnumbered" id="notes">Notes</h2>
<h2 class="unnumbered" id="extra">Extra</h2>
<h2 data-number="4.1" id="steps"><span
class="header-section-number">4.1</span> Steps</h2>
```

Intro opened at 3, and Usage followed as 4. The level-2 headings became 3.1 and 4.1. Notes and Extra stayed unnumbered.

## Plain commonmark and the attributes extension

Plain `commonmark` was next, with numbering on:

```sh
pandoc -f commonmark -t html --number-sections hnum.md
```

Its last three headings came out as:

```html
<h2 data-number="2.1"><span class="header-section-number">2.1</span>
Notes {-}</h2>
<h2 data-number="2.2"><span class="header-section-number">2.2</span>
Extra {.unnumbered}</h2>
<h2 data-number="2.3"><span class="header-section-number">2.3</span>
Steps</h2>
```

Both markers were still part of the heading text, and both headings took a number. No heading in this run had an `id`. Pandoc also broke the line in a different spot: the whole span sits on the first line and the heading text on the next.

Adding the `attributes` extension changed one of the two. This was the command:

```sh
pandoc -f commonmark+attributes -t html --number-sections hnum.md
```

The run ended with:

```html
<h2 data-number="2.1"><span class="header-section-number">2.1</span>
Notes {-}</h2>
<h2 class="unnumbered">Extra</h2>
<h2 data-number="2.2"><span class="header-section-number">2.2</span>
Steps</h2>
```

Extra lost its braces and its number. Notes kept its marker as text and took 2.1, so Steps moved to 2.2.

## Heading numbers in LaTeX output

The same test file also went through pandoc's LaTeX writer. This command wrote it:

```sh
pandoc -f markdown -t latex --number-sections hnum.md
```

The output:

```latex
\section{Intro}\label{intro}

\subsection{Setup}\label{setup}

\section{Usage}\label{usage}

\subsection*{Notes}\label{notes}
\addcontentsline{toc}{subsection}{Notes}

\subsection*{Extra}\label{extra}
\addcontentsline{toc}{subsection}{Extra}

\subsection{Steps}\label{steps}
```

No digits appear in these lines. The two marked headings became `\subsection*` lines, each with an `\addcontentsline` line under it. The run without the numbering option printed the same lines in this test. For a contents list, see the [table of contents walkthrough](markdown-table-of-contents.md).

## Check heading numbers with pandoc

Use these steps when a heading number looks wrong or a skip marker shows up as text.

1. Show the file with `cat hnum.md` and check that the markers sit at the end of their heading lines.
2. Convert it, using `pandoc -f markdown -t html --number-sections hnum.md`, and look for `header-section-number` spans.
3. Count them. `pandoc -f markdown -t html --number-sections hnum.md | grep -c 'header-section-number'` printed `4`. The same count printed `6` with `-f commonmark` and `5` with `-f commonmark+attributes`.
4. Count the skipped headings. `pandoc -f markdown -t html --number-sections hnum.md | grep -c 'class="unnumbered"'` printed `2`.
5. Drop the option and count again. `pandoc -f markdown -t html hnum.md | grep -c 'header-section-number'` printed `0`, and both commonmark settings printed `0` as well.
6. To start somewhere other than 1, add the offset option, as in `pandoc -f markdown -t html --number-sections --number-offset=2 hnum.md`, and check that the first span holds `3`.

## Where Sashmark fits

Sashmark's first version is planned around editing right where text appears once rendered. The planned clean Round-Trip would save lines you leave untouched as the very same bytes. A second planned piece, the Changes View, is set to mark changes made since your most recent read of the file, and git is planned to stay out of that step. It is in development and has not been released. The app itself has no AI features. See the [planned features](/features), the [help pages](/help) and the [roadmap](/roadmap) for where each part stands.

Project home page: [sashmark.com](https://www.sashmark.com/).
