Pipefmt formats markdown pipe tables in the browser with correct CJK width handling. The reference below enumerates the pipe-table dialects that exist in markdown today, the alignment marker syntax that GFM defines, cell-escape rules, the Unicode East Asian Width property the formatter depends on, and the common malformations that cause silent breakage in editors.
Five markdown specs define some form of pipe table. They disagree on whether outer pipes are required, what counts as a valid separator, and how multi-line cells behave.
| Dialect | Outer pipes | Separator row | Alignment markers | Multi-line cells | Notes |
|---|---|---|---|---|---|
| CommonMark | Not in core spec | N/A | N/A | N/A | Pipe tables are an extension, not part of core CommonMark |
| GitHub Flavored Markdown (GFM) | Optional per row | Required, three dashes minimum | :---, :---:, ---: | Not supported | The de-facto web standard; Pipefmt targets this dialect |
| Markdown Extra (PHP) | Optional, must be consistent | Required, three dashes minimum | :---, :---:, ---: | Not supported | First spec to add pipe tables (2004) |
| MultiMarkdown | Optional | Required | :---, :---:, ---: plus + for sortable | Supported via line continuation \ | Adds caption row syntax [Caption] |
| Pandoc pipe_tables | Required outer pipes | Required | :---, :---:, ---: | Not supported | Pandoc supports four other table syntaxes alongside pipe_tables |
Pipefmt's parser follows the GFM dialect. The markdown-table npm package,prettier-plugin-markdown, GitHub's renderer, and most static site generators do the same.
A GFM pipe table is three things in order: a header row, a separator row, and zero or more body rows. The header and separator must have the same column count. Body rows can be shorter (missing cells become empty) or longer (extra cells are dropped).
| Rule | Required? | Example |
|---|---|---|
| At least one pipe per row | Yes | Name | Age |
| Separator row immediately after header | Yes | | --- | --- | |
| Separator cell has at least three dashes | Yes | --- minimum; ---- and more are also valid |
| Outer pipes match between rows | No | Mixing is legal but visually ugly |
| Cell whitespace is trimmed | Yes | | Alice | renders the same as | Alice | |
| Pipes inside cells escaped with backslash | Yes | a \| b renders as a | b |
| Empty cells permitted | Yes | | | | | is a valid row of three empty cells |
| Header content can be empty | Yes | Renders as a header row with blank cells |
| Blank line ends the table | Yes | A blank line after a body row stops parsing |
| Tables can sit directly under prose | Yes | No blank line required above the header |
The separator row's per-column markers are the only way GFM expresses column alignment. There is no other syntax for it.
| Marker | Alignment | HTML output | Visual cue |
|---|---|---|---|
:--- | Left | <td align="left"> | Colon at the left end |
---: | Right | <td align="right"> | Colon at the right end |
:---: | Center | <td align="center"> | Colons at both ends |
--- | Default (renderer decides) | <td> | No colons |
Edge cases the GFM grammar accepts or rejects:
| Variant | Treated as | Notes |
|---|---|---|
:--: | Center | Two-dash version is legal |
:-: | Center | Single-dash version is legal |
: --- | Invalid | Separator row fails to parse (space after colon) |
--- : | Invalid | Separator row fails to parse (space before colon) |
::: | Invalid | Must contain at least one dash |
:-- | Left, but renderer-specific | GFM is lenient; some renderers reject |
Mixed within column (:--- header, ---: body) | Header wins | Only the separator row matters |
When Pipefmt's Align option is set to Auto, the formatter inspects every non-empty value in each column. Columns whose values all parse as numbers (integer or decimal, optional leading sign) get ---: (right). All others get :--- (left). Explicit markers in the input always take precedence.
| Character | Inside a cell | Output |
|---|---|---|
\| (escaped pipe) | Legal | Renders as | |
\\ (escaped backslash) | Legal | Renders as \ |
<br> | Legal in GFM | Renders as a line break |
| Literal newline | Illegal in pipe tables | Stops the table |
| Backtick code spans | Legal | Pipes inside backticks do not need escaping |
HTML entities (&) | Legal | Rendered by the host renderer, not Pipefmt |
| Trailing whitespace | Stripped | Pipefmt removes it during tokenization |
| Leading whitespace | Stripped | Same as trailing |
| Asterisks, underscores | Legal | Inline emphasis is applied by the renderer |
The formatter's CJK-aware alignment depends on Unicode Technical Report #11 (East Asian Width). Every code point in the Unicode database carries one of six width categories. Pipefmt walks each grapheme cluster with Intl.Segmenter and looks up the category for the base code point of each cluster.
| Category | Code | Monospace columns | Examples |
|---|---|---|---|
| Narrow | Na | 1 | ASCII letters, digits, punctuation |
| Wide | W | 2 | CJK Unified Ideographs, Japanese Kanji, Korean Hangul |
| Fullwidth | F | 2 | Fullwidth Latin letters (A, B), CJK symbols |
| Halfwidth | H | 1 | Halfwidth Katakana (ア, イ), halfwidth Hangul |
| Neutral | N | 1 | Most non-East-Asian scripts (Cyrillic, Devanagari, Arabic) |
| Ambiguous | A | 1 in Latin context, 2 in CJK | Greek letters, box-drawing characters, some math symbols |
Pipefmt treats Ambiguous as 1 column. This matches the string-width package defaults and produces correct output when CJK and Latin content share a column. The cost is that pure-CJK documents using Greek letters or box-drawing characters as wide content will under-pad those cells; this trade-off is the same in every East Asian Width implementation.
The Wide and Fullwidth categories cover several specific Unicode blocks. Knowing the block helps when a cell contains characters that look CJK but measure as 1 column (for example, halfwidth Katakana).
| Block | Range | Width category | Notes |
|---|---|---|---|
| CJK Unified Ideographs | U+4E00–U+9FFF | W | Most modern Chinese, Japanese Kanji, Korean Hanja |
| Hiragana | U+3040–U+309F | W | Japanese phonetic syllabary |
| Katakana | U+30A0–U+30FF | W | Japanese phonetic syllabary |
| Hangul Syllables | U+AC00–U+D7AF | W | Korean composed syllables |
| CJK Symbols and Punctuation | U+3000–U+303F | W | 、, 。, 「, 」, ideographic space |
| Halfwidth Katakana | U+FF65–U+FF9F | H | 1 column despite being Japanese |
| Fullwidth ASCII | U+FF01–U+FF5E | F | A is fullwidth, A is narrow |
| CJK Compatibility Ideographs | U+F900–U+FAFF | W | Legacy duplicate characters |
| CJK Extension A | U+3400–U+4DBF | W | Less common ideographs |
| CJK Extension B | U+20000–U+2A6DF | W | Rare ideographs, supplementary plane |
Even with correctly padded output, a font that lacks CJK glyph metrics will render the table crooked in the editor. These fonts ship with proper 2-column CJK glyphs by default.
| Font | License | CJK coverage | Source |
|---|---|---|---|
| Noto Sans Mono CJK | OFL | Full (SC, TC, JP, KR) | Google Fonts |
| Source Han Code JP | OFL | Japanese, with English ligatures | Adobe |
| Sarasa Mono | OFL | Full (SC, TC, JP, KR) | Iosevka derivative |
| JetBrains Mono + Noto CJK fallback | OFL | English primary, CJK via font-stack fallback | Configure editor font stack |
| Plemol JP | OFL | Japanese | Hybrid of IBM Plex Mono and IBM Plex Sans JP |
| Cica | OFL | Japanese | Hack + Rounded M+ |
Older Consolas builds (pre-2019) lack CJK metrics and will render wide characters as 1 column even when the source content is padded for 2. The editor font setting, not Pipefmt's output, is the cause.
A symptom-cause-fix lookup for the malformations Pipefmt sees most often.
| Symptom | Cause | Fix |
|---|---|---|
| Table shows as plain text in the rendered output | Missing or malformed separator row | Add a separator row with at least three dashes per column |
| Columns misaligned in editor despite Pipefmt running | Proportional font or missing CJK metrics in the editor font | Switch to a font from the table above |
| Trailing cells appear in the wrong column | Unescaped pipe inside a cell | Escape literal pipes with \| |
| Separator markers ignored by the renderer | Space between colon and dashes | Remove spaces; markers must be contiguous :--- |
| Header row renders as a body row | Blank line between header and separator | Remove the blank line |
| Output has an extra empty column | Trailing pipe creates an empty column | Either remove the trailing pipe everywhere or turn Outer Pipes on |
| Some rows missing cells | Body row has fewer pipes than header | Add empty cells | | to pad to the header column count |
| Multi-line content collapses | Newline inside a cell | Replace with <br> (GFM-supported) or restructure the row |
| Numbers right-align in some renderers but not others | Bare --- separator and Align is Auto in Pipefmt | Set explicit ---: markers to force right alignment everywhere |
| Cell padding looks off in a CJK column | Editor font lacks CJK metrics, or Ambiguous-category content present | Try a CJK-capable monospace font; confirm content is not Greek or box-drawing |
string-width npm package: the implementation Pipefmt's measurement is calibrated againstIntl.Segmenter: browser API for grapheme cluster iteration used in the width passpipe_tables; only pipe_tables overlaps with GFMprettier-plugin-markdown: the prettier plugin that formats markdown tables in source files