Guide · Markdown → HTML
Markdown tables to HTML tables
A pipe table is the one bit of Markdown that people most often see fail. It renders on GitHub and then comes out as a paragraph of pipes somewhere else.
This walks through the syntax that survives conversion, what the HTML looks like on the other side, and the three mistakes that turn a table back into text.
What a table needs to be a table
Pipe tables are not part of CommonMark. They come from GitHub Flavoured Markdown, which means a converter has to opt into them — and some don't. This one does.
Three things are required. A header row. A delimiter row of hyphens under it. And at least one body row. Drop any of the three and you get paragraphs.
- 01Write the header row with a pipe between each cell. The outer pipes at the start and end of the line are optional, but they make ragged tables much easier to spot.
- 02Write the delimiter row directly under it, with no blank line between. At least three hyphens per column is the safe minimum.
- 03Write your body rows. They don't have to line up in the source; the cells are split on the pipes, not on the columns.
| Part | Qty |
| ---- | --- |
| Bolt | 12 |
| Nut | 12 |<table>
<thead>
<tr>
<th scope="col">Part</th>
<th scope="col">Qty</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bolt</td>
<td>12</td>
</tr>
</tbody>
</table>The scope attribute, and why it's there
Header cells come out as <th scope="col">, not as bold <td>. That attribute is the whole reason a screen reader can announce "Qty, 12" instead of reading a bare number with no idea what column it belongs to.
It costs nothing and it's the difference between a table and a grid of numbers. A layout faked out of divs can't express it at all, which is the strongest argument against ever building one.
Alignment: the colon row
Colons in the delimiter row set alignment per column. A colon on the left aligns left, both sides centres, the right aligns right — which is what you want for numbers.
In the HTML you get a class, not an inline style: align-left, align-center or align-right. Inline styles are stripped from every output on this site, because a style attribute is the doorway CSS injection walks through.
That means full-page output has the alignment already working — the stylesheet in the <head> defines those three classes. Fragment output leaves them for your own CSS, which is the point of fragment output: three one-line rules and it matches your site instead of fighting it.
| Item | Cost |
| :--- | ---: |
| Bolt | 0.40 |<th scope="col" class="align-left">Item</th>
<th scope="col" class="align-right">Cost</th>
...
<td class="align-left">Bolt</td>
<td class="align-right">0.40</td>When the table comes out as a paragraph
Three causes, in the order they turn up.
- 01A blank line between the header and the delimiter row. That splits it into two paragraphs before the table parser ever sees it.
- 02A pipe inside a cell's text. Escape it as \| or the cell splits in two and the row ends up with more cells than the header.
- 03Not enough hyphens. A single hyphen per column works in some parsers and not others; three is the version everything agrees on.
Cells that hold more than text
Inline Markdown works inside cells: bold, italic, code spans, links. Block-level content does not — no lists, no paragraphs, no fenced code blocks. That's a limit of the table syntax itself, not of this converter.
A line break inside a cell needs a literal <br>, written by hand. Raw HTML in Markdown is passed through here rather than escaped, so it works, and it gets sanitised on the way out like everything else.
That's every part of a pipe table that behaves differently once it's HTML. The converter takes pasted Markdown or a dropped .md file, and nothing leaves your browser.
MD → HTML