Skip to content

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

  1. Write the header row. Put each column name between pipes: | Name | Role | Team |.
  2. Add the delimiter row directly below it, with one cell of hyphens per column: | --- | --- | --- |. The number of cells must match the header.
  3. Add one line per data row, using the same pipes: | Ada | Engineer | Platform |. The pipes don't have to line up.
  4. Leave a blank line before and after the table, then preview it. Escape any literal pipe in a cell as \|.
The result: a three-column table
Markdown
| Name   | Role      | Team     |
| ------ | --------- | -------- |
| Ada    | Engineer  | Platform |
| Grace  | Architect | Compiler |
| Hedy   | Designer  | Radio    |
Preview
NameRoleTeam
AdaEngineerPlatform
GraceArchitectCompiler
HedyDesignerRadio

Renders the same on GitHub, GitLab, Obsidian, VS Code and Reddit.

Open in editor : The result: a three-column table

Prefer 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.

Add a row:
Live preview
ProductCategoryIn stockPrice
KeyboardHardwareYes$129
MouseHardwareNo$79
BackupSoftwareYes$9/mo

Valid GFM table structure.

Open in the full editor

Table 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.
Minimal syntax: no outer pipes, one hyphen per cell
Markdown
Name|Role|Team
-|-|-
Ada|Engineer|Platform
Grace|Architect|Compiler
Preview
NameRoleTeam
AdaEngineerPlatform
GraceArchitectCompiler

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 cell

Column alignment

Alignment is set per column with colons in the delimiter row. The header and every cell in that column follow it.

Delimiter syntax and the alignment it produces
Delimiter cellAlignmentTypical use
---None set; renderers usually show it left-alignedGeneral text
:---LeftNames, descriptions
:---:CenterStatus icons, short codes, yes/no
---:RightNumbers, prices, percentages
All four delimiter styles in one table
Markdown
| Default | Left    | Center  | Right   |
| ------- | :------ | :-----: | ------: |
| text    | text    | text    | 1.5     |
| longer text | longer text | longer text | 1,024.75 |
Preview
DefaultLeftCenterRight
texttexttext1.5
longer textlonger textlonger text1,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.

Cell content in GitHub Flavored Markdown
ContentIn a GFM cellHowGuide
Bold, italic, strikethrough✅ Yes**bold**, *italic*, ~~strike~~Text formatting
Inline code✅ YesBackticks; escape pipes inside as \|Escaping pipes
Links✅ Yes[text](https://…) or a bare URLText formatting
Images and badges✅ Yes![alt](image.png); 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✅ YesUnicode emoji, or :shortcodes: on GitHubStatus icons
Literal pipe✅ Escaped\| or &#124;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❌ NoUse HTML in the cell, or move the content out of the tableLine breaks
Merged cells❌ NoHTML colspan / rowspanMerged cells

Formatting, code and links

Inline formatting inside cells
Markdown
| 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.

Escaped pipes in code and plain text
Markdown
| Operator  | Meaning     |
| --------- | ----------- |
| `a \| b`   | Bitwise OR  |
| `a \|\| b`  | Logical OR  |
| A \| B     | Plain text  |
Preview
OperatorMeaning
a | bBitwise OR
a || bLogical OR
A | BPlain 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 text

Line breaks and lists in a cell

<br> and an HTML list inside cells
Markdown
| Step    | Details                                  |
| ------- | ---------------------------------------- |
| Install | Run `npm install`<br>then `npm run build` |
| Ship    | <ul><li>Tag the release</li><li>Publish</li></ul> |
Preview
StepDetails
InstallRun npm install
then npm run build
Ship
  • Tag the release
  • Publish

GitHub and GitLab allow <br>, <ul> and <li> in cells. Renderers that strip HTML show the tags as text.

Open in editor : <br> and an HTML list inside cells

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 &nbsp;, 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.

Table features by Markdown flavor and platform
Flavor / platformPipe tablesHeaderlessMerged cellsCaption
GitHub (GFM)Inline Markdown, <br> and allowed HTML in cells. [ ] and [x] stay literal text.Yes: GFM specNo: Header row requiredPartly: HTML <table> onlyPartly: 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.YesNo: Header row requiredPartly: HTML tables onlyPartly: HTML tables only
CommonMark 0.31.2Tables are not in the core spec; parsers add them as an extension.No: Extension neededNot applicableNot applicableNot applicable
MultiMarkdownColumn spans with ||; a caption in [brackets] above or below the table.YesPartly: Header row expectedYes: Column spans (||)Yes: [Caption]
PandocFour syntaxes: pipe, simple, multiline and grid. Grid tables allow lists and code blocks in cells.Yes: pipe_tablesYes: Simple and multiline tablesYes: Grid tables (pandoc 3+)Yes: Table: … line
kramdown (Jekyll, GitHub Pages)Start rows with a pipe. Also has footer rows (|=) and multiple table bodies.YesYes: Omit the separator lineNoNo
PHP Markdown ExtraPopularized pipe tables and colon alignment.YesNoNoNo
ObsidianEscape the pipe in wikilink aliases inside tables: [[Note\|Alias]].YesNoPartly: HTML tablesNo
VS Code previewThe built-in preview renders GFM tables.YesNoPartly: HTML tablesPartly: HTML only
NotionPasted or imported Markdown tables are converted to Notion tables; the Markdown source is not kept.Partly: Converted on paste or importNot applicableNot applicableNot applicable
RedditUse the Markdown editor; alignment colons work.YesNoNoNo
SlackMessage formatting (mrkdwn) has no tables. Apps can send a Block Kit table block (added August 2025).No: Not in messagesNot applicableNot applicableNot applicable
DiscordNo table rendering. Put a space-aligned table inside a code block instead.No: Use a code blockNot applicableNot applicableNot 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
HTML table with a caption, row headers and a merged cellHTML
<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:

  1. Delimiter row. It must be the second line and have exactly as many cells as the header.
  2. Unescaped pipes. A | in a cell, a URL or a code span adds a column; escape it as \|.
  3. 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.
  4. Indentation. Four or more leading spaces turn the table into a code block.
  5. HTML wrappers. Inside <details> or <div>, leave a blank line after the opening tag so the table is parsed as Markdown.
  6. 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 &nbsp;, 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.

Column alignmentLeft, center and right alignment with colons, and how to align numbers.| :--- | :---: | ---: |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&nbsp;&nbsp;&nbsp; |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.| ![alt](img.png) |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.