Tables in Markdown have always been a bit of a mess. Every processor handles them slightly differently, and when you start wanting advanced features like column spans or captions, you’re usually out of luck. I’ve been working on Apex, my unified Markdown processor, and I’m happy to say that tables are now pretty solid.

The Problem With Empty Cells

One of the trickier issues I ran into was how to handle empty cells versus colspan syntax. In many table implementations, consecutive pipes (||) create a column span. But what about cells that are just… empty? You know, with some whitespace between the pipes for alignment?

Previously, Apex was a little too eager to merge cells. If you had | | (an empty cell with spaces), it might get treated as part of a colspan. That’s not what anyone wants.

Now the rule is simple and predictable:

  • || or ||| (consecutive pipes with nothing between them) = colspan
  • | | (pipes with whitespace) = empty cell

This means your carefully aligned Markdown source stays readable without accidentally merging cells.

The « Marker for Explicit Colspans

Sometimes you want a colspan but you also want your source Markdown to look nice and aligned. That’s where the new << marker comes in:

| A   | B   | C   |
| --- | --- | --- |
| D   | <<  | <<  |

Each << indicates “this cell is part of a colspan from the left.” In this example, D spans all three columns. Your source stays perfectly aligned, and you get the colspan you wanted.

Row Spans with ^^

Column spans get all the attention, but row spans are just as useful. Use ^^ in a cell to merge it with the cell above:

| Department  | Employee |
| ----------- | -------- |
| Engineering | Alice    |
| ^^          | Bob      |
| ^^          | Charlie  |

Here, “Engineering” spans three rows. Stack multiple ^^ markers and the rowspan grows accordingly.

Relaxed Tables

In unified and kramdown modes, you don’t even need a separator row:

one | two | three
1 | 2 | 3
4 | 5 | 6

Apex figures out it’s a table and renders it. No |---|---|---| required.

Headerless Tables

Sometimes you just want data without a header row. Start with an alignment row:

| --- | :---: | ---: |
| left | center | right |
| a | b | c |

The alignment row sets the column alignment, and the rest is body data. No phantom header row cluttering up your HTML.

Row Header Columns

Need semantic row headers? Leave the first header cell empty:

|       | Q1  | Q2  |
| ----- | --- | --- |
| Sales | 100 | 120 |
| Costs | 80  | 90  |

Apex renders the first column of each body row as <th scope="row">, which is exactly what screen readers expect for accessible data tables.

CSV and TSV Tables

Don’t feel like typing all those pipes? Use a fenced code block with the table info string:

```table
header 1,header 2,header 3
data 1,data 2,data 3
```

Apex auto-detects whether it’s CSV or TSV and converts it to a proper table. You can even include an alignment row using keywords like left, center, right.

Multiple Caption Formats

Captions can go before or after tables, and Apex supports several syntaxes:

[My Table Caption]

| A | B |
|---|---|
| 1 | 2 |

Or Pandoc-style:

| A | B |
|---|---|
| 1 | 2 |

Table: My Table Caption

Or the Pandoc table attributes extension:

| A | B |
|---|---|
| 1 | 2 |

: My Caption {#table-id .my-class}

That last one even lets you attach IAL attributes directly to the table through the caption. Use --captions above or --captions below to control placement.

Table Footers

Need a footer section? Use a row of ===:

| Item   | Price |
| ------ | ----: |
| Widget | 10.00 |
| Gadget | 25.00 |
| ====== | ===== |
| Total  | 35.00 |

Everything after the === row goes into <tfoot>.

Per-Cell Alignment

Override column alignment on individual cells using colons:

| A      | B       | C        |
| ------ | ------- | -------- |
| :Left  | Right:  | :Center: |

The colons are stripped from the output and replaced with inline styles.

Wrapping Up

Tables were one of the areas where I really wanted Apex to shine. Between relaxed parsing, multiple colspan/rowspan syntaxes, CSV support, captions, footers, and per-cell alignment, I think it covers pretty much every table scenario I’ve ever needed.

Check out the Apex repository and the Tables documentation for more details. And if you find edge cases I haven’t covered, open an issue on GitHub. I’m always looking to make tables even better.