Skip to content

How to Merge Cells in a Markdown Table (Colspan and Rowspan)

By MarkdownTables.com editorial teamUpdated

Smallest merged table: a header spanning two columnsHTML
<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

Where you write the table → how to merge cells
PlatformMethodSyntax
GitHub, GitLab, VS Code, JupyterHTML table<td colspan="2">, <td rowspan="2">
ObsidianHTML table, or the Table Extended plugin|| across, ^^ down
MultiMarkdownBuilt-in column spans| Long cell ||
Pandoc, QuartoGrid tables (pandoc 3.0+)Leave out the | or +---+ border between merged cells
MkDocs (Python-Markdown)mdx_spanner extension~~ merges left, __ merges up
markdown-it sitesmarkdown-it-merge-cells, or markdown-it-multimd-tableIdentical adjacent cells merge / MultiMarkdown ||
remark / rehype, Docusaurusrehype-extended-table> merges right, ^ merges up
HugoHTML table with unsafe = true<td colspan="2">
Reddit, Discourse, NotionNot possibleRepeat 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.

Two-row header with merged cellsHTML
<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>
Renders as
Region2025
H1H2
Europe1.2M1.4M
AmericasReported 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.

Vertical merge with rowspanHTML
<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>
Renders as
LayerToolVersion
FrontendReact19
Tailwind CSS4
BackendNode.js22

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

Markdown inside a merged cell (GitHub, GitLab)HTML
<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.

MultiMarkdown colspan with ||
Markdown
|             | Grouping                    ||
| First       | Second        | Third        |
| ----------- | :-----------: | -----------: |
| Content     | *Long cell*                 ||
| Content     | **Cell**      | Cell         |
Preview
Grouping
FirstSecondThird
ContentLong cell
ContentCellCell

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.

Pandoc grid table with spansMarkdown
+---------------------+----------+
| 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.

Table Extended syntax (Obsidian)
Markdown
| Layer    | Tool         | Version |
| -------- | ------------ | ------- |
| Frontend | React        | 19      |
| ^^       | Tailwind CSS | 4       |
| Backend  | Node.js and Postgres  ||
Preview

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.

mdx_spanner syntax (MkDocs)
Markdown
| Layer    | Tool         | Version |
| -------- | ------------ | ------- |
| Frontend | React        | 19      |
| __       | Tailwind CSS | 4       |
| Backend  | Node.js      | ~~      |
Preview

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.
Markdown Preview Enhanced / rehype-extended-table syntax
Markdown
| Layer    | Tool         | Version |
| -------- | ------------ | ------- |
| Frontend | React        | 19      |
| ^        | Tailwind CSS | 4       |
| Backend  | >            | Node.js |
Preview

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.
Repeat the value instead of merging
Markdown
| Layer    | Tool         | Version |
| :------- | :----------- | ------: |
| Frontend | React        |      19 |
| Frontend | Tailwind CSS |       4 |
| Backend  | Node.js      |      22 |
Preview
LayerToolVersion
FrontendReact19
FrontendTailwind CSS4
BackendNode.js22
Bold group rows instead of a merged column
Markdown
| 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

Merged cells by platform
PlatformSpans in Markdown syntaxHTML colspan / rowspan
GitHub (GFM)HTML tables keep colspan and rowspan.NoYes
GitLab (GLFM)Same as GitHub.NoYes
ObsidianMarkdown inside HTML is not rendered.Partly: Table Extended pluginYes
VS Code previewThe built-in preview renders HTML tables.Partly: Markdown Preview Enhanced extensionYes
JupyterMarkdown cells render HTML tables.NoYes
Pandoc / QuartoGrid tables with row and column spans since pandoc 3.0.Yes: Grid tablesPartly: HTML output only
MultiMarkdownColumn spans with ||; no row spans.Partly: Colspan onlyYes
MkDocsPython-Markdown passes HTML through.Partly: mdx_spannerYes
DocusaurusMDX: write colSpan / rowSpan to avoid React warnings.Partly: rehype pluginYes
HugoGoldmark drops raw HTML by default.NoPartly: Needs unsafe = true
Discoursemarkdown-it based; spans are not supported.NoNo: Attributes dropped
RedditNo HTML.NoNo
NotionNotion tables have no merge option.NoNot 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

Broken: the text stays in one column and the last cell is empty
Markdown
| Region   | H1   | H2   |
| -------- | ---- | ---- |
| Europe   | 1.2M | 1.4M |
| Americas | Reported annually |
Preview
RegionH1H2
Europe1.2M1.4M
AmericasReported annually

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

Broken: an extra cell under the rowspanHTML
<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>
Fixed: the covered cell is removedHTML
<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.