Markdown Table Syntax: How to Make a Table in Markdown
By MarkdownTables.com editorial teamUpdated
On this page
How to create a table in Markdown
- Write the header row. Put each column name between pipes:
| Name | Role | Team |. - Add the delimiter row directly below it, with one cell of hyphens per column:
| --- | --- | --- |. The number of cells must match the header. - Add one line per data row, using the same pipes:
| Ada | Engineer | Platform |. The pipes don't have to line up. - Leave a blank line before and after the table, then preview it. Escape any literal pipe in a cell as
\|.
| Name | Role | Team |
| ------ | --------- | -------- |
| Ada | Engineer | Platform |
| Grace | Architect | Compiler |
| Hedy | Designer | Radio || Name | Role | Team |
|---|---|---|
| Ada | Engineer | Platform |
| Grace | Architect | Compiler |
| Hedy | Designer | Radio |
Renders the same on GitHub, GitLab, Obsidian, VS Code and Reddit.
Open in editor : The result: a three-column tablePrefer clicking to typing pipes? The Markdown table generator builds the same syntax from a spreadsheet-style grid, and the converters turn CSV, Excel or HTML tables into Markdown.
Try it: live table playground
Edit the source below. The preview follows GitHub's rules and tells you when the structure would stop the table from rendering.
| Product | Category | In stock | Price |
|---|---|---|---|
| Keyboard | Hardware | Yes | $129 |
| Mouse | Hardware | No | $79 |
| Backup | Software | Yes | $9/mo |
Valid GFM table structure.
Open in the full editorTable structure rules
These are the rules from the GFM specification that decide whether text becomes a table. Other flavors follow them closely.
- The header and delimiter rows must have the same number of cells. If they don't, nothing is rendered as a table.
- Each delimiter cell needs at least one hyphen, with an optional colon at either end. Spaces around the hyphens are fine.
- Data rows may have fewer or more cells than the header: missing cells render empty, and extra cells are dropped.
- Outer pipes are optional in GFM, but keep them: they make the source easier to read, and some parsers (such as kramdown) expect rows to start with a pipe. A one-column table needs them.
- Spacing doesn't matter. Padding cells so the pipes line up is only for people reading the source.
- The table ends at the first blank line. In GFM, a plain line directly under a table becomes one more row, so always end a table with a blank line.
Name|Role|Team
-|-|-
Ada|Engineer|Platform
Grace|Architect|Compiler| Name | Role | Team |
|---|---|---|
| Ada | Engineer | Platform |
| Grace | Architect | Compiler |
Valid GFM and renders like a padded table. Run it through the table formatter to make the source readable.
Open in editor : Minimal syntax: no outer pipes, one hyphen per cellColumn alignment
Alignment is set per column with colons in the delimiter row. The header and every cell in that column follow it.
| Delimiter cell | Alignment | Typical use |
|---|---|---|
--- | None set; renderers usually show it left-aligned | General text |
:--- | Left | Names, descriptions |
:---: | Center | Status icons, short codes, yes/no |
---: | Right | Numbers, prices, percentages |
| Default | Left | Center | Right |
| ------- | :------ | :-----: | ------: |
| text | text | text | 1.5 |
| longer text | longer text | longer text | 1,024.75 || Default | Left | Center | Right |
|---|---|---|---|
| text | text | text | 1.5 |
| longer text | longer text | longer text | 1,024.75 |
Right-align numeric columns so digits line up. Markdown has no decimal alignment, so give numbers the same number of decimal places. More detail in the column alignment guide.
What you can and can't put in a cell
A cell holds one line of inline content. Anything that would start a new block (a heading, a list, a code fence, a block quote, a second paragraph) ends the row instead.
| Content | In a GFM cell | How | Guide |
|---|---|---|---|
| Bold, italic, strikethrough | ✅ Yes | **bold**, *italic*, ~~strike~~ | Text formatting |
| Inline code | ✅ Yes | Backticks; escape pipes inside as \| | Escaping pipes |
| Links | ✅ Yes | [text](https://…) or a bare URL | Text formatting |
| Images and badges | ✅ Yes | ; size with <img width="…"> | Images and badges |
| Line breaks | ✅ With HTML | <br> | Line breaks |
| Bulleted lists | ⚠️ HTML only | <ul><li>…</li></ul> on one line (GitHub, GitLab), or • with <br> | Line breaks |
| Emoji | ✅ Yes | Unicode emoji, or :shortcodes: on GitHub | Status icons |
| Literal pipe | ✅ Escaped | \| or | | Escaping pipes |
| Math | ⚠️ Some platforms | $…$ on GitHub and GitLab; write \vert instead of | | Math and LaTeX |
| Task checkboxes | ❌ Literal text | [x] shows as text on GitHub; use ✅ / ☐ | Checkboxes |
| Markdown lists, headings, code fences, block quotes | ❌ No | Use HTML in the cell, or move the content out of the table | Line breaks |
| Merged cells | ❌ No | HTML colspan / rowspan | Merged cells |
Formatting, code and links
| Syntax | Example |
| ----------- | ------------------------------- |
| Bold | **Important** |
| Italic | *Optional* |
| Strike | ~~Deprecated~~ |
| Code | `npm install` |
| Link | [Docs](https://example.com) |
| Emoji | ✅ Done |Escaping a pipe character
An unescaped | always starts a new cell, even inside backticks, because GFM splits the row into cells before it looks for code spans. Write \|; the backslash is not shown in the output.
| Operator | Meaning |
| --------- | ----------- |
| `a \| b` | Bitwise OR |
| `a \|\| b` | Logical OR |
| A \| B | Plain text || Operator | Meaning |
|---|---|
a | b | Bitwise OR |
a || b | Logical OR |
| A | B | Plain text |
GitHub removes the backslash inside code spans too. Renderers that don't follow the GFM spec closely may show it; see escaping pipes.
Open in editor : Escaped pipes in code and plain textLine breaks and lists in a cell
<br> and an HTML list inside cells| Step | Details |
| ------- | ---------------------------------------- |
| Install | Run `npm install`<br>then `npm run build` |
| Ship | <ul><li>Tag the release</li><li>Publish</li></ul> || Step | Details |
|---|---|
| Install | Run npm installthen npm run build |
| Ship |
|
GitHub and GitLab allow <br>, <ul> and <li> in cells. Renderers that strip HTML show the tags as text.
Limitations and workarounds
Pipe tables trade features for readable source. Each gap has a standard workaround:
- Merged cells. GFM has no colspan or rowspan. Switch to an HTML table, or use MultiMarkdown (
||) or Pandoc grid tables. Merged cells guide. - Column width. Widths follow the content and there is no width syntax. Pad a header with
, add an<img width="…">spacer, or use HTML with CSS where it's allowed. Column width guide. - Captions. GFM has none. Put a bold line or a heading above the table, use an HTML
<caption>, or use Pandoc (Table: …) or MultiMarkdown ([…]). Captions guide. - Headerless tables. GFM always treats the first row as the header. Use blank header cells, or kramdown and Pandoc, which allow tables without one. Headerless tables.
- Nested tables. A Markdown table can't contain another table. Split the data into two tables, link to a detail table, or write the outer table in HTML.
- Long or block content. Use
<br>for short multi-line cells, hide big tables in a collapsible section, or transpose a wide table so it reads top to bottom.
Markdown table support by platform
Basic pipe tables are portable. The differences show up in the extras: headerless tables, merged cells and captions.
| Flavor / platform | Pipe tables | Headerless | Merged cells | Caption |
|---|---|---|---|---|
GitHub (GFM)Inline Markdown, <br> and allowed HTML in cells. [ ] and [x] stay literal text. | Yes: GFM spec | No: Header row required | Partly: HTML <table> only | Partly: HTML <caption> only |
GitLab (GLFM)GitLab 18.9+ renders [x], [ ] and [~] as checkboxes when alone in a cell. json:table code blocks build sortable tables from JSON. | Yes | No: Header row required | Partly: HTML tables only | Partly: HTML tables only |
| CommonMark 0.31.2Tables are not in the core spec; parsers add them as an extension. | No: Extension needed | Not applicable | Not applicable | Not applicable |
MultiMarkdownColumn spans with ||; a caption in [brackets] above or below the table. | Yes | Partly: Header row expected | Yes: Column spans (||) | Yes: [Caption] |
| PandocFour syntaxes: pipe, simple, multiline and grid. Grid tables allow lists and code blocks in cells. | Yes: pipe_tables | Yes: Simple and multiline tables | Yes: Grid tables (pandoc 3+) | Yes: Table: … line |
kramdown (Jekyll, GitHub Pages)Start rows with a pipe. Also has footer rows (|=) and multiple table bodies. | Yes | Yes: Omit the separator line | No | No |
| PHP Markdown ExtraPopularized pipe tables and colon alignment. | Yes | No | No | No |
ObsidianEscape the pipe in wikilink aliases inside tables: [[Note\|Alias]]. | Yes | No | Partly: HTML tables | No |
| VS Code previewThe built-in preview renders GFM tables. | Yes | No | Partly: HTML tables | Partly: HTML only |
| NotionPasted or imported Markdown tables are converted to Notion tables; the Markdown source is not kept. | Partly: Converted on paste or import | Not applicable | Not applicable | Not applicable |
| RedditUse the Markdown editor; alignment colons work. | Yes | No | No | No |
| SlackMessage formatting (mrkdwn) has no tables. Apps can send a Block Kit table block (added August 2025). | No: Not in messages | Not applicable | Not applicable | Not applicable |
| DiscordNo table rendering. Put a space-aligned table inside a code block instead. | No: Use a code block | Not applicable | Not applicable | Not applicable |
✅ supported · ⚠️ partly or with a workaround · ❌ not supported · Last verified October 2026
“Merged cells” and “Caption” marked as HTML-only work when you write the whole table in HTML instead of pipe syntax.
Markdown tables vs. HTML tables
Most Markdown renderers accept raw HTML, so you can always fall back to a <table>. Stay with Markdown when the data is a plain grid that people edit as text, such as READMEs, docs in Git and changelogs, because pipe tables are easy to read in the source and in diffs. Switch to HTML when you need any of these:
- merged cells (
colspan,rowspan) - row headers (
<th scope="row">) or a<caption> - block content in cells: lists, code blocks or several paragraphs
- fixed column widths or styling, on renderers that keep attributes
<table>
<caption>Quarterly revenue by region</caption>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col">Q1</th>
<th scope="col">Q2</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Europe</th>
<td>$1.2M</td>
<td>$1.4M</td>
</tr>
<tr>
<th scope="row">Americas</th>
<td colspan="2">Reported annually</td>
</tr>
</tbody>
</table>Inside an HTML table, Markdown such as **bold** is only processed if a blank line separates it from the tags, so it is usually simpler to use HTML (<b>, <code>) there. GitHub strips style attributes, so CSS colors and widths won't survive. To convert in either direction, use Markdown table to HTML or HTML table to Markdown.
Why a Markdown table doesn't render
When the pipes show up as plain text, check these in order:
- Delimiter row. It must be the second line and have exactly as many cells as the header.
- Unescaped pipes. A
|in a cell, a URL or a code span adds a column; escape it as\|. - Blank line before the table. GitHub can start a table right after a paragraph, but kramdown, many site generators and linters (markdownlint MD058) expect a blank line.
- Indentation. Four or more leading spaces turn the table into a code block.
- HTML wrappers. Inside
<details>or<div>, leave a blank line after the opening tag so the table is parsed as Markdown. - Platform support. Discord, Slack messages and core-CommonMark renderers never draw tables.
The table validator flags each of these with the line number, and the troubleshooting guide covers platform-specific cases.
Where the table syntax comes from
John Gruber's original Markdown (2004) has no table syntax; you wrote tables in HTML. Michel Fortin's PHP Markdown Extra popularized the pipe table with a dashed separator line and colon alignment, and MultiMarkdown, Pandoc and kramdown adopted and extended the idea with column spans, captions and other table styles.
GitHub made pipe tables part of GitHub Flavored Markdown. The GFM spec (version 0.29-gfm) defines them precisely as the “Tables (extension)” on top of CommonMark. CommonMark itself, currently version 0.31.2, still has no tables, so CommonMark parsers such as markdown-it, remark (with remark-gfm) and cmark-gfm add them as an extension.
One detail often causes confusion: GitHub Docs say each delimiter cell needs at least three hyphens, but the GFM spec accepts one (|-|-|). Three is a readable convention, not a requirement.
Accessible Markdown tables
A Markdown table becomes a real HTML table, so screen readers can move through it by row and column. A few habits keep it usable:
- Write meaningful header cells. The header row becomes
<th>cells that are read out with each value. Blank headers (the headerless workaround) leave cells without context. - Know that GFM has no row headers. In a comparison table where the first column names each row, only HTML can mark those cells as
<th scope="row">. - Introduce the table. GFM has no caption, so put a heading or a sentence right before the table that says what it contains, or use an HTML
<caption>where allowed. - Avoid empty and symbol-only cells. Write “None” or “N/A” rather than leaving a cell blank, and pair icons with words (“✅ Yes”), because emoji are read out by name.
- Use tables for data, not layout. Don't use a table to put images side by side or to indent text.
Frequently asked questions
Does Markdown support tables?
Not in John Gruber's original Markdown or in core CommonMark, but almost every modern flavor does: GitHub Flavored Markdown, GitLab, Obsidian, MultiMarkdown, Pandoc and kramdown all render pipe tables. Discord and Slack messages are the main places where they don't render.
How do I make a table in Markdown without a header?
GitHub Flavored Markdown requires a header row. The usual workaround is a header of empty cells (| | |) or , which renders as a thin blank header. kramdown (Jekyll, GitHub Pages) and Pandoc simple or multiline tables support headerless tables natively. See headerless tables and captions.
How do I add a line break in a table cell?
Use the HTML <br> tag, for example | Line one<br>Line two |. Pressing Enter ends the row, so a real newline cannot be used. <br> works on GitHub, GitLab, Obsidian and most renderers that allow inline HTML. See line breaks in tables.
Can you merge cells in a Markdown table?
Not in GitHub Flavored Markdown, which has no colspan or rowspan. Use an HTML <table> with colspan or rowspan (GitHub and GitLab render it), MultiMarkdown's || for column spans, or Pandoc grid tables. See merged cells.
Why is my Markdown table not rendering?
Usually the delimiter row is missing or has a different number of cells than the header, an unescaped | adds an extra column, there is no blank line before the table, or the platform does not support tables at all. Paste the table into the table validator to find the line.
Do Markdown tables work in all editors?
Most do: GitHub, GitLab, VS Code, Obsidian, Typora, Jupyter and static site generators all render pipe tables. Renderers that implement only core CommonMark, Discord, and Slack messages do not. The platform support table lists the differences.
How do I align text in a Markdown table column?
Add colons to the delimiter row: :--- aligns left, :---: centers, and ---: aligns right. A plain --- leaves the alignment to the renderer, which is usually left. See column alignment.
How many hyphens does the delimiter row need?
The GFM spec requires only one hyphen per cell, so |-|-| is valid. GitHub Docs recommend three (---), which is the common convention and easier to read. Extra hyphens are allowed and are often used to line columns up.
All table syntax guides and tools
Each guide covers one part of the syntax in depth, with platform notes and copyable examples.
| :--- | :---: | ---: |Escaping pipe charactersPut a literal | in a cell or code span without adding a column.| `a \| b` |Line breaks in cellsMulti-line cells and lists inside a cell with <br>.| Line 1<br>Line 2 |Text formatting and colorBold, italic, strikethrough, code, sub/superscript and GitHub-safe color.| **bold** | ~~old~~ |Merged cellsColspan and rowspan with HTML, MultiMarkdown and Pandoc grid tables.<td colspan="2">Column widthWhy widths follow content, and the workarounds that hold.| Name |Headerless tables and captionsTables without a header row, and how to title a table.| | |Checkboxes and status iconsWhy [x] stays literal in cells, and what to use instead.| ✅ | ☐ |Images and badgesImages, shields.io badges and avatars in cells, with sizing.|  |Collapsible tablesHide long tables in <details> on GitHub and GitLab.<details><summary>Math and LaTeXInline math in cells without breaking columns.| $\vert x \vert$ |Lists in cellsBullets and numbered lists inside a cell with inline HTML.| <ul><li>One</li></ul> |Code in tablesInline code, escaped pipes in code and multi-line code in cells.| `a \| b` |Nested tablesWhy pipe tables cannot nest, and the HTML and layout alternatives.<td><table>Borders, colors and widthStyling comes from the renderer: what you can change where.table { border: 1px }Sortable tablesWhich platforms sort tables, and how to sort the source.| A ↕ | B ↕ |Tools
Also see the printable Markdown table cheatsheet and the platform guides for GitHub, GitLab, Obsidian, VS Code, Pandoc, Python and React.