CSV to Markdown Table

Paste a few rows or drop a CSV and get a pipe table you can put straight into a README, an issue or a docs page. Pipes inside cells are escaped, number columns line up on the right.

Rows to sort or filter before the table? Open the app

Written for whoever is writing the docs

Typing a pipe table by hand is fine for three rows and miserable for twenty. The data almost always exists somewhere already, which is what this page is for:

  • README tables. Configuration options, CLI flags, environment variables, supported versions. The kind of table a project gains one row at a time and nobody wants to realign.
  • Issues and pull requests. Benchmark numbers before and after, a compatibility matrix, the list of files a migration touches. Pasting a table reads far better than an attachment nobody opens.
  • Docs sites. MkDocs, Docusaurus, VitePress and Astro all take markdown, and their table content is usually maintained in a sheet by someone who is not going to edit a docs repo.
  • Notes and wikis. Obsidian, Logseq and Notion all paste markdown tables cleanly. So does a Slack canvas.
  • Design and architecture records. An options table in an ADR, a risk register in a proposal. Both start as a spreadsheet and end up in a repository.

The result panel shows markdown source rather than a rendered table, on purpose. Source is what you are about to paste, and it is what you need to be able to eyeball.

Worked example: a schema table for a docs page

Four columns, one of which contains a pipe, one of which is numeric with gaps:

column,type,nullable,max length
id,integer,no,
email,text | citext,no,320
nickname,text,yes,64
created_at,timestamptz,no,

Out comes this, ready to paste:

| column | type | nullable | max length |
| --- | --- | --- | ---: |
| id | integer | no |  |
| email | text \| citext | no | 320 |
| nickname | text | yes | 64 |
| created_at | timestamptz | no |  |

Three details worth pointing at. The pipe in text | citext came through as \|, so the row still has four cells instead of five. The max length column got ---: because 320 and 64 are the only values in it and both are plain numbers, so the digits line up on the right. And the two rows with no maximum length have an empty cell rather than a placeholder, because inventing a dash or an n/a would be inventing data.

Rendered, that is a table with 4 rows and 4 columns, which is what the result panel reports underneath. schema.csv downloads as schema.md.

Three characters that break a pipe table

A markdown table has almost no syntax, which is why it is pleasant to read and easy to destroy. Exactly three things in your data can do it, and all three are handled rather than warned about:

  • A pipe ends the cell. Any | in a value is written as \|. This is the one that catches people out most, because pipes turn up constantly in documentation: alternatives in a type, a union, a shell command, a regex.
  • A backslash escapes whatever follows. Backslashes are doubled before the pipes are escaped, so a Windows path such as C:\Users\me|home becomes C:\\Users\\me\|home. Done in the other order, a value ending in a backslash would eat the escape on the pipe after it and split the row anyway.
  • A newline ends the row. A cell holding two lines becomes one cell with a <br> between them. GitHub renders that as a line break. A plain-text reader shows the tag, which is a compromise, but markdown offers no continuation syntax at all so the alternative is a broken table.

Header cells go through exactly the same escaping, so a column named a|b does not quietly become two columns.

Alignment, and what this page will not do

Column alignment has two settings. Numbers right is the default: a column whose non-empty values are all plain numbers gets ---:, everything else gets ---. It is unanimous or nothing, so a single n/a in a column of order counts pushes the whole column back to the left, and so does a leading zero, since 02215 is an identifier rather than a quantity. All left gives every column plain dashes, which is what you want when a table is going somewhere the alignment markers are noise.

Being straight about the rest of it, since these are the gaps people actually run into:

  • GFM pipe tables only. No grid tables with plus signs and full borders, no HTML tables, no column spans or merged cells. Pipe tables cannot express those, and pretending otherwise produces something that renders nowhere.
  • One file at a time. There is no folder mode. A directory of CSVs is a job for a script, not a browser tab.
  • Columns are not padded. Cells are joined with single spaces rather than aligned to a common width. Every renderer ignores padding, and a padded table makes a horrible diff when one value gets longer and every row shifts.
  • Past ten thousand rows it says so. The table is still written, with a note that most viewers render something that size slowly and nobody scrolls that far. A markdown table is a document element, not a data store.
  • Cell contents are not markdown-escaped beyond those three characters. Asterisks, underscores and backticks in your data will be interpreted as formatting by the renderer. That is usually what people want in a docs table, and it is worth knowing if it is not.
  • Duplicate headers are renamed, not merged. Two columns called id become id and id_2 with a note, so no column disappears from the table.

Everything happens in your tab, with no upload endpoint behind the page. Comma, semicolon, tab and pipe delimited files are all read without configuration, so a copy of cells straight out of a spreadsheet works in the paste box. The limit is 100 MB with no row cap, though a table that size belongs somewhere other than a markdown file.

Frequently Asked Questions

What flavour of markdown table does it write?

Standard GitHub Flavored Markdown pipe tables: a header row, a row of dashes, then one row per record. That is what GitHub, GitLab, Bitbucket, MkDocs, Docusaurus, Obsidian, Notion and every static site generator with a table extension will render. Grid tables, the reStructuredText style with plus signs and full borders, are not produced, and neither are HTML tables.

What happens to a pipe character inside a cell?

It is escaped as a backslash followed by a pipe, so the cell survives instead of splitting the row in two. Backslashes are escaped first, which matters more than it sounds: without that, a cell ending in a backslash would swallow the escape on the pipe after it and the row would break anyway.

Can a cell contain more than one line?

Not really, and that is markdown's limit rather than ours. A pipe table row ends at the newline, with no continuation syntax available. A line break inside a cell is written as a br tag, which GitHub and most renderers turn into a real break; a plain-text reader shows the tag itself. It is not perfect, and it beats a table that falls apart at that row.

Why is one column right aligned and another is not?

On the default setting, a column is right aligned only when every non-empty value in it is a plain number, which is when lining the digits up actually helps. One value like n/a, or a leading zero such as 02215, makes the column left aligned along with the rest. Switch Column alignment to All left and every column gets three dashes instead.

Why are the columns not padded to the same width?

Because it costs bytes and buys nothing. Every renderer ignores the padding, a wide file gains a great deal of trailing whitespace, and a padded table produces an unreadable diff when one value changes and every row shifts. The output is compact source; the rendered table looks identical either way.

Can I convert several CSV files at once?

No, this page handles one file at a time. If you have a folder of them, a short script around a markdown library will serve you better. This is built for the case where a table is going into a document you are writing now, which is almost always one file.

Make your markdown table

Paste the rows or drop the CSV, glance at the source, then copy it into your README. No sign-up, no upload, no row cap.

Back to the converter