Pandoc Markdown Tables: Captions, Grid Tables and Column Widths
By MarkdownTables.com editorial teamUpdated
On this page
How do I add a caption to a table in Pandoc?
GitHub Flavored Markdown has no table captions, but Pandoc's table_captions extension (on by default in -f markdown) does. The caption is a normal paragraph that begins with Table: or a bare :, placed directly before or after the table with a blank line in between. Inline Markdown such as *emphasis* works in it, and it can wrap over several lines.
The preview follows GitHub, which shows the Table: line as an ordinary paragraph. Pandoc renders it as the table's caption.
pandoc planets.md -o planets.html
pandoc planets.md -o planets.pdf # LaTeX: numbered "Table 1: …" caption
pandoc planets.md -o planets.docx # Word: caption paragraph above the tableIn HTML the caption becomes <caption>; in LaTeX/PDF it is numbered (“Table 1:”) and appears in \listoftables; in Word it uses the Table Caption style. To label and cross-reference tables, use Quarto ({#tbl-name}, below) or the pandoc-crossref filter ({#tbl:name}); plain Pandoc leaves the braces in the caption as text. Other caption options across flavors are in headerless tables and captions.
Pandoc’s four table syntaxes compared
All four syntaxes are enabled by default when Pandoc reads markdown. They differ in what a cell can hold and how easy they are to type:
| Syntax | Extension | Wrapped text in cells | Merged cells | Headerless | Renders on GitHub |
|---|---|---|---|---|---|
| Pipe | pipe_tables | No (one line per row) | No | No | Yes |
| Simple | simple_tables | No | No | Yes | No |
| Multiline | multiline_tables | Yes | No | Yes | No |
| Grid | grid_tables | Yes, plus lists and code blocks | Yes (Pandoc 3.0+) | Yes | No |
Pipe tables
The same syntax as GitHub (see Markdown table syntax): a header row, a delimiter row with optional colons for alignment, one line per row. Use pipe tables when the file must also render on GitHub, GitLab or in an editor preview.
| Option | Default | Meaning |
|:--------------|:-------:|---------------------------:|
| `--columns` | 72 | Line length for wrapping |
| `--wrap` | auto | How text lines are wrapped || Option | Default | Meaning |
|---|---|---|
--columns | 72 | Line length for wrapping |
--wrap | auto | How text lines are wrapped |
Simple tables
Columns are defined by a dashed line under the header; alignment comes from where the header text sits relative to the dashes (flush right = right-aligned, centered = centered). Each row is one line.
Right Left Center Default
------- ------ ---------- -------
12 12 12 12
123 123 123 123
1 1 1 1
Table: Demonstration of simple table syntax.Leave out the header, and close the table with a second dashed line, to get a headerless table:
------- ------ ---------- -------
12 12 12 12
123 123 123 123
------- ------ ---------- -------Multiline tables
Multiline tables start and end with a full-width dashed line, and rows are separated by blank lines, so a cell can wrap over several source lines. The width of each column's dashes sets its relative width in the output.
-------------------------------------------------------------
Centered Default Right Left
Header Aligned Aligned Aligned
----------- ------- --------------- -------------------------
First row 12.0 Example of a row that
spans multiple lines.
Second row 5.0 Here's another one. Note
the blank line between
rows.
-------------------------------------------------------------
Table: Here's the caption. It, too, may span
multiple lines.Grid tables: merged cells and block content
Grid tables draw every cell with +, - and |. A row of = separates the header, and colons in that row set alignment. Cells hold any block content: paragraphs, lists, code blocks. Since Pandoc 3.0, leaving out a border creates a merged cell: no | between two cells spans columns, and no +---+ line between rows spans rows.
: Temperature extremes, 1961–1990.
+---------------------+----------+
| Property | Earth |
+=============+=======+==========+
| | min | -89.2 °C |
| Temperature +-------+----------+
| 1961-1990 | mean | 14 °C |
| +-------+----------+
| | max | 56.7 °C |
+-------------+-------+----------+Here “Property” spans two columns and “Temperature 1961-1990” spans three rows; Pandoc writes colspan/rowspan in HTML and \multicolumn/\multirow in LaTeX.
+-----------+------------------------------+
| Step | Details |
+:==========+:=============================+
| Install | - Download the binary |
| | - Add it to `PATH` |
+-----------+------------------------------+
| Verify | ``` |
| | pandoc --version |
| | ``` |
+-----------+------------------------------+Pandoc 3 grid tables can also have a footer: rows below a second = separator become <tfoot>.
+-----------+----------+
| Region | Revenue |
+:==========+=========:+
| EMEA | 1.2M |
+-----------+----------+
| APAC | 0.8M |
+===========+==========+
| Total | 2.0M |
+===========+==========+Grid tables are tedious to type. Write the data as a pipe table and let Pandoc convert it with pandoc in.md -t markdown-pipe_tables-simple_tables-multiline_tables, or use an editor mode that redraws grids (Emacs table.el or org-mode, or a grid-table extension for VS Code).
Column widths and the --columns option
Pandoc decides column widths differently per syntax. For pipe tables, the rule is: if any line of the table source is longer than --columns (default 72), the table takes the full text width and each column gets a share proportional to the number of dashes in its delimiter cell. If every line fits, columns size to their content.
| Flag | Description |
|-----:|:------------------------------------|
| `-s` | Produce a standalone document with a header and footer, using the default template for the output format. || Flag | Description |
|---|---|
-s | Produce a standalone document with a header and footer, using the default template for the output format. |
In HTML or PDF output, Pandoc gives the first column about 12% and the second about 88% of the width. GitHub ignores the dashes.
Open in editor : A long row triggers relative widths: 5 dashes vs. 36 dashes| Syntax | Width rule | To control it |
|---|---|---|
| Pipe | Relative to dashes, only when a line exceeds --columns | Adjust dash counts; raise or lower --columns |
| Simple | Natural (sized to content) | Switch to multiline or grid |
| Multiline | Relative to the column width in the source | Widen or narrow the dashed columns |
| Grid | Relative to the column width in the source | Redraw the grid |
| Quarto | tbl-colwidths overrides the above | tbl-colwidths="[75,25]" after the caption |
In LaTeX output, wrapped columns become p{…} columns of a longtable, so long tables break across pages. For finer control in PDF, convert the table once to LaTeX with the Markdown table to LaTeX tool and edit it there.
Converting tables to PDF, DOCX, HTML and GFM
# HTML with a full page around it
pandoc report.md -s -o report.html
# PDF through LaTeX; xelatex or lualatex handle Unicode such as °C and emoji fonts
pandoc report.md -o report.pdf --pdf-engine=xelatex
# Word, styled by your own template's "Table" style
pandoc report.md -o report.docx --reference-doc=custom-reference.docx
# Back to GitHub Markdown (grid/multiline tables become pipe tables where possible)
pandoc report.md -t gfm -o README.md
# Rewrap or normalize tables in Pandoc Markdown
pandoc report.md -t markdown --columns=100 -o report.normalized.md- PDF. Pandoc goes through LaTeX by default (
pdflatex). The default template loadslongtable,booktabsandmultirow, so captions, footers and spans work. Use--pdf-engine=xelatexorlualatexwhen cells contain non-Latin characters. - DOCX. Tables get Word's “Table” style. To change borders or shading, edit that style in a reference document (
pandoc -o custom-reference.docx --print-default-data-file reference.docx) and pass it with--reference-doc. - HTML. Captions become
<caption>; relative widths become a<colgroup>with percentage widths. Alignment is written as inlinetext-alignstyles. - GitHub Markdown.
-t gfmwrites pipe tables, puts the caption in a paragraph after the table, splits merged cells into empty ones, and writes an HTML<table>when a cell holds a list or code block.
For the reverse direction, pandoc page.html -t gfm turns HTML tables into pipe tables, or use the HTML table to Markdown converter for a single table.
Quarto: tbl-cap, tbl-colwidths and @tbl- references
Quarto runs Pandoc, so all four syntaxes and Table: captions work. It adds labels, numbering and cross-references. Put the caption below the table with a #tbl- label at the end, then refer to it with @tbl-…:
| Fruit | Price |
|--------|------:|
| Apple | 0.50 |
| Mango | 1.75 |
: Fruit prices {#tbl-fruit tbl-colwidths="[75,25]"}
Prices are listed in @tbl-fruit.tbl-colwidths takes percentages per column. Set it per table after the caption, for the whole document in YAML, or in a code cell:
---
title: "Report"
tbl-colwidths: [60, 40] # default for every table in the document
---```{r}
#| label: tbl-cars
#| tbl-cap: "Stopping distance by speed"
#| tbl-colwidths: [50, 50]
knitr::kable(head(cars))
```Quarto's docs note that shortcodes don't work inside grid tables and suggest list tables instead when cells need complex content.
Tables from R with knitr::kable
In R Markdown and Quarto, knitr::kable() is the quickest way to turn a data frame into a Markdown table. With format = "pipe" it writes a pipe table with a Table: caption, which Pandoc then converts to any output:
knitr::kable(
head(mtcars[, 1:4]),
format = "pipe",
digits = 1,
caption = "Fuel economy and engine size"
)kable writes row names as an unlabeled first column; pass row.names = FALSE to drop them.
For the same job in Python, df.to_markdown() is covered in Markdown tables in Python; add the Table: line yourself.
What survives in each output format
| Output | Caption | Column widths | Lists/code in cells | Merged cells |
|---|---|---|---|---|
HTML (-t html)Caption becomes <caption>; widths become <colgroup>. | Yes | Yes | Yes | Yes |
PDF via LaTeXlongtable with booktabs; spans use \multicolumn / \multirow. | Yes: Numbered | Yes | Yes | Yes |
| Word (.docx)Uses the reference doc’s Table and Table Caption styles. | Yes | Yes | Yes | Yes |
| GitHub Markdown (-t gfm)Plain tables become pipe tables; complex ones fall back to raw HTML. | Partly: Paragraph after the table | Partly: Only in the HTML fallback | Partly: HTML fallback | Partly: Spans lost in pipe output |
QuartoPandoc underneath, plus #tbl- labels and tbl-colwidths. | Yes: Numbered, cross-referenced | Yes | Yes | Yes |
✅ supported · ⚠️ partly or with a workaround · ❌ not supported · Last verified October 2026
Tested against Pandoc 3.6. Merged cells require grid tables; lists and code in cells require grid tables.
Common mistakes and fixes
Padding a grid cell with spaces turns it into code
Grid cells are parsed as Markdown blocks, so four or more leading spaces make an indented code block. Don't right-align numbers by padding; put colons in the = separator row instead.
+--------+----------+
| Region | Revenue |
+========+==========+
| EMEA | 1.2M |
+--------+----------++--------+----------+
| Region | Revenue |
+:=======+=========:+
| EMEA | 1.2M |
+--------+----------+Other mistakes
- Caption ignored. Missing blank line, or the input format is
gfmorcommonmark, which don't havetable_captions. Use-f markdown. - Ragged grid lines. Every
|and+must line up exactly; one shifted character and the grid becomes a paragraph. Use a monospace editor and a grid-aware mode. - Unexpected full-width table. A long pipe-table row exceeded
--columns. Shorten it, set--columns=200, or balance the dashes deliberately. - Merged cells vanish. Pandoc older than 3.0 reads grid tables without spans; check
pandoc --version. - Unicode errors in PDF. Switch to
--pdf-engine=xelatex.
Frequently asked questions
How do I add a caption to a Markdown table in Pandoc?
Put a paragraph starting with Table: (or just :) directly before or after the table, separated by a blank line: Table: Revenue by region. Pandoc turns it into a real caption in HTML, LaTeX/PDF and Word. It works for pipe, simple, multiline and grid tables.
Can you merge cells in a Pandoc table?
Yes, with grid tables in Pandoc 3.0 and later. Leave out the | or +---+ border between cells to make one cell span columns or rows. Spans are kept in HTML, LaTeX and DOCX output. Pipe, simple and multiline tables have no spans.
How do I set column widths for a Pandoc table?
In pipe tables, widths come from the dashes in the delimiter row, but only when a line is longer than --columns (default 72). In multiline and grid tables, the width of each column in the source sets its relative width. In Quarto, use tbl-colwidths="[75,25]" after the caption.
Which Pandoc table format should I use?
Pipe tables for anything that also needs to render on GitHub or GitLab. Multiline tables when cells need wrapped text. Grid tables when you need merged cells, lists or code blocks in cells. Simple tables for small, quick, plain-text-readable tables.
Why does my Pandoc table caption not show up?
Check for a blank line between the table and the Table: line, that the input format is Pandoc Markdown (-f markdown; -f gfm and -f commonmark have no captions), and that the caption starts with Table: or :.
How do I reference a table in Quarto?
Add a label that starts with tbl- in braces at the end of the caption, : Fruit prices {#tbl-fruit}, then write @tbl-fruit in the text. For tables from code cells, use #| label: tbl-name and #| tbl-cap: "Caption".
Does Pandoc convert grid tables to GitHub Markdown?
Yes, with pandoc -t gfm. Simple grids become pipe tables, but merged cells are split into empty cells, and cells with lists or code blocks make Pandoc write the whole table as HTML, which GitHub renders.
Related: merged cells, math in tables, rendering tables in React and the Markdown table to HTML converter.