Where Markdown tables are used
Typing a Markdown table by hand is tedious: every row needs pipes, the delimiter row needs dashes, and columns drift out of alignment as soon as a value changes. Generating it from CSV is faster and less error-prone. Typical uses:
- Benchmark results or a feature matrix in a GitHub README or pull request description.
- Release notes, runbooks and documentation in GitLab, Confluence-compatible wikis, Obsidian or Docusaurus.
- Pasting spreadsheet data into a chat or issue tracker that renders Markdown.
The output follows GitHub Flavored Markdown (GFM) table syntax, which nearly every modern Markdown renderer supports.
How the table is built
The CSV is parsed with RFC 4180 rules, so quoted commas and doubled quotes are handled before any Markdown is produced. Then:
- Every column is padded to the width of its longest value, so the raw Markdown reads as a table too. Width is measured visually, so CJK characters and emoji line up.
- Columns whose non-empty values are all numeric —
129.90,-12,1,234.50,3.5e4,12%— are right-aligned with a---:delimiter; text columns stay left-aligned. - Rows shorter than the widest row are padded with empty cells, because a GFM table needs the same number of cells in every row.
- Pipe characters inside values are escaped as
\|and backslashes as\\, so they cannot break the table. - Line breaks inside quoted cells become
<br>, since a Markdown table row must stay on one line.
Values are not reformatted: dates, IDs and decimals appear exactly as in the CSV.
First row is a header
Markdown tables always have a header row. With First row is a header on (the default), the CSV’s first row supplies the column titles. Turn it off when the CSV has no header: the table then gets generic titles Column 1, Column 2 and so on, and every CSV row becomes a body row. Rename the titles afterwards in the generated Markdown.
Limits of Markdown tables
GFM tables cannot merge cells, span rows, or hold block content such as lists or code blocks; cells contain inline text only. Very wide tables will scroll or wrap depending on the renderer, so consider dropping columns you do not need before converting. Inline Markdown inside cells — **bold** or [links](https://example.com) — is passed through untouched and will render.
If you need styling, borders or HTML features, use CSV to HTML table instead. Preview the result with the Markdown formatter, or convert it to HTML with Markdown to HTML. The work is done in your browser either way.
Examples
Benchmark results for a README
The two numeric columns are right-aligned, and the quoted comma in the last note stays inside its cell.
library,ops/sec,p99 (ms),notes
fast-json,1240000,0.8,default build
json-bigint,310000,3.1,keeps 64-bit ints
yaml,95000,12.4,"YAML 1.2, strict"
| library | ops/sec | p99 (ms) | notes |
| ----------- | ------: | -------: | ----------------- |
| fast-json | 1240000 | 0.8 | default build |
| json-bigint | 310000 | 3.1 | keeps 64-bit ints |
| yaml | 95000 | 12.4 | YAML 1.2, strict |
Cells with pipes and line breaks
The pipe in the command is escaped so it does not split the cell, and the two-line description is joined with <br>.
command,description
ls | wc -l,"Counts files
in the current directory"
grep -c,Counts matching lines
| command | description |
| ----------- | ---------------------------------------- |
| ls \| wc -l | Counts files<br>in the current directory |
| grep -c | Counts matching lines |
CSV without a header
Generic Column 1 to Column 3 titles are added and all three rows appear in the table body.
JSON,MVP,shipped
YAML,MVP,shipped
TOML,v2,planned
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| JSON | MVP | shipped |
| YAML | MVP | shipped |
| TOML | v2 | planned |
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
This quoted field is never closedExplained | A CSV field opens a quote that never closes, so the rest of the input would be one cell. | Close the quote, doubling any quotes that belong inside the value. |
Row has 2 fields, expected 4; the missing fields are left emptyExplained | A warning: a row is shorter than the header. The table pads it with empty cells. | Check for missing commas in that row if the empty cells are unexpected. |
Table renders as plain text with pipes | The Markdown renderer does not support GFM tables, or the table was pasted without a blank line before it. | Leave an empty line above the table, and confirm the platform supports GitHub Flavored Markdown. |
Frequently asked questions
Why are some columns right-aligned?
Columns where every non-empty value looks like a number get a —: delimiter so digits line up, which is easier to compare.
Can a Markdown table cell contain a line break?
Not literally, so line breaks are converted to <br>, which GitHub and most renderers display as a new line.
What if my data contains the | character?
It is escaped as | so the renderer shows a pipe instead of starting a new column.
Does this work for GitHub, GitLab and Notion?
Yes. They all accept the GFM table syntax produced here. Notion converts it to a native table when pasted.