How to Merge Cells in a Markdown Table (Colspan and Rowspan)
By MarkdownTables.com editorial teamUpdated
<table>
<tr><th>Region</th><th colspan="2">2025</th></tr>
<tr><td>Europe</td><td>1.2M</td><td>1.4M</td></tr>
</table>On this page
How to merge cells on each platform
| Platform | Method | Syntax |
|---|---|---|
| GitHub, GitLab, VS Code, Jupyter | HTML table | <td colspan="2">, <td rowspan="2"> |
| Obsidian | HTML table, or the Table Extended plugin | || across, ^^ down |
| MultiMarkdown | Built-in column spans | | Long cell || |
| Pandoc, Quarto | Grid tables (pandoc 3.0+) | Leave out the | or +---+ border between merged cells |
| MkDocs (Python-Markdown) | mdx_spanner extension | ~~ merges left, __ merges up |
| markdown-it sites | markdown-it-merge-cells, or markdown-it-multimd-table | Identical adjacent cells merge / MultiMarkdown || |
| remark / rehype, Docusaurus | rehype-extended-table | > merges right, ^ merges up |
| Hugo | HTML table with unsafe = true | <td colspan="2"> |
| Reddit, Discourse, Notion | Not possible | Repeat values or split the table |
Merging with an HTML table (GitHub, GitLab)
Most Markdown renderers accept raw HTML, so an HTML <table> is the portable answer. The two attributes:
colspan="N"merges a cell horizontally across N columns. The row then has N−1 fewer cells.rowspan="N"merges a cell vertically down N rows. Each of the next N−1 rows leaves that cell out.
GitHub keeps both attributes, along with align and valign, but strips style, so you can't color or size the merged cell with CSS there. To start from an existing pipe table, convert it with Markdown table to HTML and add the spans by hand.
How do I merge header cells?
A group header over several columns is the most common reason to merge. Pipe tables allow exactly one header row, so this needs HTML: a colspan for the group and a rowspan for the header that sits beside both rows.
<table>
<thead>
<tr>
<th rowspan="2">Region</th>
<th colspan="2">2025</th>
</tr>
<tr>
<th>H1</th>
<th>H2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Europe</td>
<td>1.2M</td>
<td>1.4M</td>
</tr>
<tr>
<td>Americas</td>
<td colspan="2">Reported annually</td>
</tr>
</tbody>
</table>| Region | 2025 | |
|---|---|---|
| H1 | H2 | |
| Europe | 1.2M | 1.4M |
| Americas | Reported annually | |
How do I merge cells vertically?
For a vertical merge, put rowspan on the first cell and delete that column's cell from the rows it covers. Here “Frontend” spans two rows, so the Tailwind row has only two cells.
<table>
<tr>
<th>Layer</th>
<th>Tool</th>
<th>Version</th>
</tr>
<tr>
<td rowspan="2">Frontend</td>
<td>React</td>
<td>19</td>
</tr>
<tr>
<td>Tailwind CSS</td>
<td>4</td>
</tr>
<tr>
<td>Backend</td>
<td>Node.js</td>
<td>22</td>
</tr>
</table>| Layer | Tool | Version |
|---|---|---|
| Frontend | React | 19 |
| Tailwind CSS | 4 | |
| Backend | Node.js | 22 |
Using Markdown inside the HTML cells
Inside an HTML table, Markdown is treated as plain text unless it sits in its own paragraph. On GitHub and GitLab, leave a blank line after the opening <td> and before </td>; the content between is then parsed as Markdown. Otherwise use HTML tags (<b>, <code>, <a href>).
<table>
<tr>
<th>Command</th>
<th>Notes</th>
</tr>
<tr>
<td colspan="2">
**Setup** — run `npm ci` first, see [the docs](https://example.com)
</td>
</tr>
</table>Obsidian doesn't render Markdown inside HTML at all, and in MDX the blank-line trick behaves differently, so use HTML tags there.
Markdown syntaxes that support spans
MultiMarkdown: column spans with ||
MultiMarkdown (and tools built on it) merges a cell with the next column when it ends with an extra pipe. There is no row span.
||| | Grouping ||
| First | Second | Third |
| ----------- | :-----------: | -----------: |
| Content | *Long cell* ||
| Content | **Cell** | Cell || Grouping | ||
|---|---|---|
| First | Second | Third |
| Content | Long cell | |
| Content | Cell | Cell |
MultiMarkdown result. GitHub would render the || as an empty extra cell instead.
Pandoc grid tables: row and column spans
Since pandoc 3.0, grid tables support both spans: leave out the vertical border between cells to merge across, and the horizontal border to merge down. This example from the pandoc manual has a header spanning two columns and a cell spanning three rows.
+---------------------+----------+
| Property | Earth |
+=============+=======+==========+
| | min | -89.2 °C |
| Temperature +-------+----------+
| 1961-1990 | mean | 14 °C |
| +-------+----------+
| | max | 56.7 °C |
+-------------+-------+----------+HTML output keeps the spans. How other formats (LaTeX, DOCX) handle them depends on the writer and your pandoc version, so check the output. The Pandoc tables guide covers grid tables in detail.
Plugins: Obsidian, MkDocs, markdown-it, remark
If you control the Markdown pipeline, a plugin keeps tables readable as pipe syntax and still produces real spans. Each one uses its own markers, so the source is not portable.
Obsidian: Table Extended
The community plugin Table Extended adds MultiMarkdown table syntax: || merges across and a cell of ^^ merges with the cell above. Its README warns that it doesn't work with Advanced Tables, whose auto-formatting breaks the markers.
| Layer | Tool | Version |
| -------- | ------------ | ------- |
| Frontend | React | 19 |
| ^^ | Tailwind CSS | 4 |
| Backend | Node.js and Postgres ||Renders with merged cells only when the plugin is enabled.
MkDocs: mdx_spanner
The Python-Markdown extension mdx_spanner (add it under markdown_extensions in mkdocs.yml) merges a cell containing only ~~ into the cell on its left, and a cell containing only __ into the cell above.
| Layer | Tool | Version |
| -------- | ------------ | ------- |
| Frontend | React | 19 |
| __ | Tailwind CSS | 4 |
| Backend | Node.js | ~~ |Renders with merged cells only when the extension is enabled.
markdown-it and remark
- markdown-it-merge-cells needs no markers: it merges adjacent cells with identical content, first down each column and then along each row. Convenient for generated tables, surprising if two neighbouring values happen to match.
- markdown-it-multimd-table implements MultiMarkdown tables, including
||column spans and optional^^row spans. - rehype-extended-table (remark/rehype pipelines, Docusaurus) follows the Markdown Preview Enhanced syntax: a
>cell merges into the cell on its right and a^cell into the cell above. The VS Code extension Markdown Preview Enhanced supports the same syntax when its extended table option is on.
| Layer | Tool | Version |
| -------- | ------------ | ------- |
| Frontend | React | 19 |
| ^ | Tailwind CSS | 4 |
| Backend | > | Node.js |Renders with merged cells only when the plugin is enabled.
No HTML allowed? Pure Markdown fallbacks
On Reddit, Discourse, in Notion, or where a linter bans HTML, you can't merge. These layouts carry the same information:
- Repeat the value in every row it applies to. It reads well, survives sorting and filtering, and is the most accessible option.
- Group rows under a bold label row, which looks like a merged section header.
- Split the table into one table per group, each under its own heading.
| Layer | Tool | Version |
| :------- | :----------- | ------: |
| Frontend | React | 19 |
| Frontend | Tailwind CSS | 4 |
| Backend | Node.js | 22 || Layer | Tool | Version |
|---|---|---|
| Frontend | React | 19 |
| Frontend | Tailwind CSS | 4 |
| Backend | Node.js | 22 |
| Tool | Version |
| :----------- | ------: |
| **Frontend** | |
| React | 19 |
| Tailwind CSS | 4 |
| **Backend** | |
| Node.js | 22 |Avoid ditto marks (", ^) and arrows (→) as fake merges: screen readers read them out literally, and sorting the table makes them point at the wrong row.
Merged cell support by platform
| Platform | Spans in Markdown syntax | HTML colspan / rowspan |
|---|---|---|
GitHub (GFM)HTML tables keep colspan and rowspan. | No | Yes |
| GitLab (GLFM)Same as GitHub. | No | Yes |
| ObsidianMarkdown inside HTML is not rendered. | Partly: Table Extended plugin | Yes |
| VS Code previewThe built-in preview renders HTML tables. | Partly: Markdown Preview Enhanced extension | Yes |
| JupyterMarkdown cells render HTML tables. | No | Yes |
| Pandoc / QuartoGrid tables with row and column spans since pandoc 3.0. | Yes: Grid tables | Partly: HTML output only |
MultiMarkdownColumn spans with ||; no row spans. | Partly: Colspan only | Yes |
| MkDocsPython-Markdown passes HTML through. | Partly: mdx_spanner | Yes |
DocusaurusMDX: write colSpan / rowSpan to avoid React warnings. | Partly: rehype plugin | Yes |
| HugoGoldmark drops raw HTML by default. | No | Partly: Needs unsafe = true |
| Discoursemarkdown-it based; spans are not supported. | No | No: Attributes dropped |
| RedditNo HTML. | No | No |
| NotionNotion tables have no merge option. | No | Not applicable |
✅ supported · ⚠️ partly or with a workaround · ❌ not supported · Last verified October 2026
“HTML colspan / rowspan” means writing the whole table as an HTML <table>. Plugins must be installed and enabled by the site owner.
Common mistakes (broken vs. fixed)
Leaving out a pipe to “merge” in a pipe table
| Region | H1 | H2 |
| -------- | ---- | ---- |
| Europe | 1.2M | 1.4M |
| Americas | Reported annually || Region | H1 | H2 |
|---|---|---|
| Europe | 1.2M | 1.4M |
| Americas | Reported annually |
GFM pads short rows with empty cells; it never spans.
Open in editor : Broken: the text stays in one column and the last cell is emptyThe fix is the HTML table with colspan="2" shown above.
Keeping the covered cell in the next row
With rowspan or colspan, the cells you merged over must be deleted. Leaving them in pushes the row one column to the right and adds a ragged extra column.
<tr>
<td rowspan="2">Frontend</td>
<td>React</td>
</tr>
<tr>
<td></td> <!-- this cell is already covered by the rowspan -->
<td>Tailwind CSS</td>
</tr><tr>
<td rowspan="2">Frontend</td>
<td>React</td>
</tr>
<tr>
<td>Tailwind CSS</td>
</tr>Markdown on the same line as the tag
<td>**Total**</td> shows the asterisks. Use <td><b>Total</b></td>, or the blank-line layout from Markdown inside HTML cells.
Which method should you use?
- README or docs on GitHub/GitLab: HTML table. Keep it short, because it is harder to edit than pipe syntax.
- Pandoc or Quarto documents: grid tables, which also allow lists and paragraphs in cells.
- Your own site or vault with many merged tables: a plugin, accepting that the source only renders correctly in that pipeline.
- Data people will sort, diff or convert: don't merge. Repeat the values; converters to CSV, JSON or SQL need one value per cell anyway.
Going the other way, the HTML table to Markdown converter turns an HTML table into pipe syntax; a colspan value is repeated across the columns it covers and a rowspan value comes out once with the cells below it left empty, so review those rows and repeat values where needed. For the rest of the syntax, see the Markdown table syntax guide, and for multi-line content inside a cell, line breaks in tables. Wide merged tables can also go in a collapsible section.
Frequently asked questions
Can you merge cells in a Markdown table?
Not in GitHub Flavored Markdown: pipe tables have no colspan or rowspan. Write the table in HTML with colspan or rowspan (GitHub and GitLab render it), or use MultiMarkdown ||, Pandoc grid tables, or a plugin.
How do I merge cells vertically in a Markdown table?
Use rowspan in an HTML table: <td rowspan="2">Frontend</td>, then leave that cell out of the next row. Pandoc grid tables and plugins such as Table Extended (^^) and mdx_spanner (__) also merge vertically.
How do I merge cells horizontally (merge columns)?
Use colspan: <td colspan="2">Reported annually</td> in an HTML table. In MultiMarkdown, end the cell with an extra pipe: | Long cell || spans two columns.
How do I merge header cells in a Markdown table?
Write the header in HTML: <th colspan="2">2025</th> for a group header across two columns, and <th rowspan="2"> for a header that sits beside a two-row header. Pipe tables have exactly one header row, so there is no Markdown way.
How do I merge cells in an Obsidian table?
Obsidian tables have no merge option. Install the community plugin Table Extended, which adds MultiMarkdown spans (|| across, ^^ down), or write an HTML table. The plugin does not get along with Advanced Tables, whose formatter rewrites the span markers.
Can I use Markdown inside an HTML table cell?
On GitHub and GitLab, yes, if you leave a blank line after <td> and before </td>. Without the blank lines **bold** shows literally, so use <b> or <code> there. Obsidian never renders Markdown inside HTML.
Does merging cells hurt accessibility?
Real colspan and rowspan are fine: screen readers announce the span. Fake merges are worse: blank cells are read as "blank" and arrows or ditto marks are read literally. Repeating the value is the most accessible pure-Markdown fallback.