---
title: "Markdown citations: keys, references and citeproc in pandoc"
slug: markdown-citations
excerpt: "Markdown citations as pandoc 3.8.3 reads them; the section A references block with citeproc quotes HTML built from one YAML entry."
quickAnswer: "Grep counts for citation words across the spec text, with a control count, are given in the section Citation syntax in plain CommonMark. Under Citation keys in the markdown reader, pandoc's HTML for a bracketed key and a bare key is quoted from one run. Output after adding the citeproc option, including a reference list, appears in A references block with citeproc. A run on a file with no references block, and its warning, is reported under A key with no matching entry. The section Turning the citations extension off shows the same file with the extension removed. Results from the commonmark reader, with spec line numbers, fill Reading the file as commonmark. Under Check a citation with pandoc and grep, numbered steps give grep results for pandoc runs."
keyTakeaways:
  - "Counts for citation and cite from a spec grep, plus one control, sit in the section Citation syntax in plain CommonMark."
  - "Pandoc's HTML for both keys, with no extra option, sits in Citation keys in the markdown reader."
  - "Formatted text and one reference entry from a citeproc run are shown in A references block with citeproc."
  - "Output for two keys with the extension removed is quoted under Turning the citations extension off."
  - "How the commonmark reader handled the YAML lines is reported in Reading the file as commonmark."
publishedDate: 2026-10-20
dateModified: 2026-10-20
author: Bobby Huang
cluster: B
role: post
status: published
faq:
  - q: Has CommonMark got citation syntax?
    a: "Under Citation syntax in plain CommonMark, grep counts taken on the spec file appear beside a control count."
  - q: My citation key prints as typed. Why?
    a: "Under Turning the citations extension off, the HTML is quoted for a run with that extension removed."
  - q: Can pandoc build a reference list from Markdown?
    a: "Under A references block with citeproc, the citeproc run's HTML is quoted in full."
  - q: What happens when a citation key has no entry?
    a: "The warning and HTML from that case appear in the section A key with no matching entry."
related:
  - what-is-front-matter
  - yaml-front-matter-in-markdown
  - markdown-footnotes
---

You typed `[@doe2020, p. 3]` into a Markdown note to point at a source, and now you want to know what that bracket turns into. Pandoc's answer in this test changed with the reader setting and with one command-line option.

Here, one small file meets pandoc 3.8.3 (macOS build), a single reader setting at a time, and the post quotes what came back. It covers markdown citations written as keys, a YAML `references:` block, the `--citeproc` option, a key with no entry, and the `commonmark` reader. Commands sit beside the output they made. No viewer was tested here; the checks end at pandoc's output.

## Citation syntax in plain CommonMark

No citation syntax turned up when grep searched the 0.31.2 spec text from CommonMark. With case ignored, grep found `citation` on 0 lines and `cite` on 0 lines. The stem `bibliograph` matched 1 line: line 5671, where the spec mentions bibliography entries while it talks about emphasis nested inside emphasis. The control term `heading` matched 67 lines under that same case-ignored search.

Pandoc adds the syntax through its `citations` extension. The command `pandoc --list-extensions=markdown | grep citations` printed `+citations` here, so the `markdown` reader has it switched on. Run as `grep -c citations` on the `commonmark` list, it printed `0`. A control grep for `attributes` on that list counted 1.

Footnotes are a separate pandoc feature with their own syntax; the [footnote syntax and fixes](markdown-footnotes.md) post covers them.

## Citation keys in the markdown reader

The test file `cite.md` opens with a YAML block that lists one synthetic source, then has two cited lines:

```markdown
---
references:
- id: doe2020
  type: book
  author:
  - family: Doe
    given: Jane
  issued:
    date-parts:
    - [2020]
  title: Plain Text Notes
  publisher: Doe Holdings
---

As shown [@doe2020, p. 3].

@doe2020 says so.
```

The first cited line puts the key inside square brackets, with `p. 3` after a comma. The second opens a sentence with the bare key.

With no extra option, `pandoc -f markdown -t html cite.md` gave:

```html
<p>As shown <span class="citation" data-cites="doe2020">[@doe2020, p.
3]</span>.</p>
<p><span class="citation" data-cites="doe2020">@doe2020</span> says
so.</p>
```

Both keys became a `span` with the class `citation` and a `data-cites` attribute holding the key. Both spans hold the typed characters, brackets and at sign included; in the first, pandoc broke the line after `p.`, so `3]` starts a new line. The period after the closing bracket landed outside the span. The YAML block printed nothing in the body, and no reference list came out of this run.

## A references block with citeproc

Adding `--citeproc` changed the result. `pandoc -f markdown --citeproc -t html cite.md` gave:

```html
<p>As shown <span class="citation" data-cites="doe2020">(Doe 2020,
3)</span>.</p>
<p><span class="citation" data-cites="doe2020">Doe (2020)</span> says
so.</p>
<div id="refs" class="references csl-bib-body hanging-indent"
role="list">
<div id="ref-doe2020" class="csl-entry" role="listitem">
Doe, Jane. 2020. <em>Plain Text Notes</em>. Doe Holdings.
</div>
</div>
```

Three things changed. The bracketed key became `(Doe 2020, 3)`: the family name, the year and the page number inside parentheses, with the `p.` gone. The bare key became `Doe (2020)`, with the name outside the parentheses. And a `div` with the id `refs` appeared after the text. It holds one entry built from the YAML fields: family name, given name, year, the title inside `<em>`, and the publisher.

The span tags kept the same `class` and `data-cites` values as in the run without the option; the text inside them changed. The command named no style file, so this entry format is what pandoc printed by default in this test.

The YAML block in `cite.md` is front matter: it opens the file. For what such a block holds, see [a front matter primer](what-is-front-matter.md), and for the YAML rules inside it, [YAML key rules](yaml-front-matter-in-markdown.md).

## A key with no matching entry

An added test, `cite-noref.md`, held one line and no YAML block:

```markdown
As shown [@doe2020, p. 3].
```

The log for `pandoc -f markdown --citeproc -t html cite-noref.md` held a warning and then the HTML:

```text
[WARNING] Citeproc: citation doe2020 not found
<p>As shown <span class="citation"
data-cites="doe2020">(<strong>doe2020?</strong>)</span>.</p>
```

The key came back in bold with a question mark after it, inside parentheses. The `p. 3` from the input did not appear in this output. The run still ended with exit code 0, so the missing entry did not stop pandoc.

Without `--citeproc`, the same file gave the typed text inside a citation span, matching the first line from `cite.md` in the run without the option.

## Turning the citations extension off

A minus sign and an extension name after the reader name removed that extension in this test. `pandoc -f markdown-citations -t html cite.md` gave the two lines below. This copy is ASCII-normalized: in the first line, pandoc wrote U+00A0 (a no-break space) between `p.` and `3`, and the copy shows U+0020 (a plain space) in its place.

```html
<p>As shown [@doe2020, p. 3].</p>
<p>@doe2020 says so.</p>
```

No span came out. The at signs stayed in both lines and the brackets stayed in the first; the second line matched the input. The first line differed from the input by that one character: the space after `p.` came back as U+00A0. A grep for `citation` on that output counted 0 lines. The YAML block printed nothing in the body here either.

## Reading the file as commonmark

The `commonmark` reader got the same `cite.md`. `pandoc -f commonmark -t html cite.md` gave:

```html
<hr />
<p>references:</p>
<ul>
<li>id: doe2020 type: book author:
<ul>
<li>family: Doe given: Jane issued: date-parts:
<ul>
<li>[2020] title: Plain Text Notes publisher: Doe Holdings</li>
</ul></li>
</ul></li>
</ul>
<hr />
<p>As shown [@doe2020, p. 3].</p>
<p>@doe2020 says so.</p>
```

The opening `---` line became `<hr />`. Spec lines 838 to 841 say a line of three or more matching `-`, `_` or `*` characters forms a thematic break. The lines that start with `- ` became nested list items; the spec names `-`, `+` and `*` as bullet list markers (lines 3808 to 3809). The indented field lines were folded into the item text, as the quoted output shows. The closing `---` came out as `<hr />` too.

Below the second rule, the two cited lines printed as typed, with no span and no reference list.

## Check a citation with pandoc and grep

When a key prints as typed, test that one line alone.

1. Copy the cited line, plus its `references:` block if it has one, into a new file. In this test, `cat cite-noref.md` printed the single line `As shown [@doe2020, p. 3].`
2. Count citation spans. `pandoc -f markdown -t html cite.md | grep -c 'class="citation"'` printed `2`, one for each key in the file.
3. Check for a reference list. `pandoc -f markdown --citeproc -t html cite.md | grep -c 'csl-entry'` printed `1`. Dropping `--citeproc` from that command made the count `0`.
4. Read any warning line. For `cite-noref.md`, which had no `references:` block, pandoc printed `[WARNING] Citeproc: citation doe2020 not found` in this test.
5. Rule out the reader. `pandoc -f markdown-citations -t html cite.md | grep -c 'citation'` printed `0`. Swap in the second reader and count rules: `pandoc -f commonmark -t html cite.md | grep -c '<hr'` printed `2`, one for each `---` line read as a rule.
6. If your counts differ from these, compare your output line by line with the HTML quoted for the same command above; the copy under Turning the citations extension off is ASCII-normalized.

## Where Sashmark fits

Plans for Sashmark's v1 would let you edit the rendered page directly, and a clean Round-Trip, also planned, should keep each untouched line identical, byte for byte. A Changes View on the same plan, with no git needed, would highlight edits made since your previous reading. 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.

Follow the build at [sashmark.com](https://www.sashmark.com/).
