MD096 - Table rows should be sorted by a named column¶
Aliases: table-sort
Enabled by default: No (opt-in)
Fixable: Yes, unless the table contains HTML comments.
Configuration¶
You can also enable the rule with extend-enable = ["MD096"] under [global].
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false |
Enable this opt-in rule. Alternatively, use extend-enable. |
column |
string | Unset | Column heading to sort by. No tables are sorted until this is supplied. |
order |
string | "asc" |
Ascending ("asc") or descending ("desc"); "ASC" and "DESC" are also accepted. |
The configured column name is plain text. Matching ignores case and surrounding whitespace:
column = "Name" matches Name, NAME, and **Name** in a table header.
An unset or whitespace-only column does nothing; there is no implicit first-column sort.
Examples¶
With column = "Name", this table is out of order:
| Name | Plan |
| --- | --- |
| [Zulu](https://example.com/z) | Free |
| [Alpha][alpha] | Paid |
[alpha]: https://example.com/a
The fix moves complete body rows and leaves the header and delimiter unchanged:
| Name | Plan |
| --- | --- |
| [Alpha][alpha] | Paid |
| [Zulu](https://example.com/z) | Free |
[alpha]: https://example.com/a
Sorting behavior¶
- Keys use the cell's text: link labels, image alt text, and code contents contribute; emphasis markers, HTML tags, and link destinations do not.
- Inline math uses its contents, following rumdl's parser extensions.
- Defined full, collapsed, and shortcut reference links use their labels. Undefined reference syntax remains literal text. Obsidian wikilinks use the alias when present.
- Keys are trimmed and converted to Unicode lowercase, then compared lexically. This is deterministic, locale-independent ordering, not language-specific collation or full Unicode case folding. Accented letters can sort after ASCII letters.
- Numbers are compared as text:
10precedes2in ascending order. - Empty cells sort first in ascending order and last in descending order.
- Equal keys retain their original order in both directions.
- Cell source, row spacing, and pipe style are preserved. LF and CRLF files retain their line-ending style; mixed endings follow the shared fixer's normalization policy. MD060 can format the sorted table separately.
Scope and exceptions¶
Sorting changes the order readers see. This is why the rule is opt-in and requires
an explicitly named column. Every matching table is selected, including tables
in different sections of the same file. A Name column in a sponsors table might
be intentionally ordered by sponsorship tier; exempt that table if appropriate:
<!-- rumdl-disable MD096 -->
| Name | Tier |
| --- | --- |
| Zulu | Gold |
| Alpha | Silver |
<!-- rumdl-enable MD096 -->
A disable on any line from the header through the last body row suppresses the
whole table's sorting diagnostic and fix. The table-sort alias also works in
disable comments. HTML comments inside a table prevent automatic sorting, even
when they configure another rule; a diagnostic is still available for manual review.
Only parser-confirmed pipe tables are checked. The rule skips tables when:
- the configured heading is absent or matches more than one column;
- any body row has a different cell count from the header;
- the parser recognizes additional body rows without pipes beyond the scanned table;
- there are fewer than two body rows;
- body rows have different container prefixes, such as mixed explicit and lazy blockquote continuations, or do not occupy consecutive source lines.
Tables inside lists and blockquotes are supported when their body prefixes are
consistent. Code examples, raw HTML blocks, and malformed delimiters are left alone.
MD056 can repair cell counts and MD075 can repair orphaned rows before sorting.
Cell boundaries follow the Markdown flavor: an unescaped pipe inside a code span
is a separator in GFM, while MkDocs treats it as code content. Escape code-span
pipes (\|) in GFM tables to avoid ambiguous or extra columns.