<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Naoki's Notes]]></title><description><![CDATA[Naoki's Notes]]></description><link>https://m-naoki-m.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Naoki&apos;s Notes</title><link>https://m-naoki-m.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 07 Sep 2026 23:53:21 GMT</lastBuildDate><atom:link href="https://m-naoki-m.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[7 ways a Markdown table silently breaks (and the 60-second checklist)]]></title><description><![CDATA[Markdown tables have no error messages. When one breaks, the renderer does not tell you why — it just dumps your pipes and dashes as plain text, or quietly eats a column. I run a browser-side converte]]></description><link>https://m-naoki-m.hashnode.dev/7-ways-a-markdown-table-silently-breaks-and-the-60-second-checklist</link><guid isPermaLink="true">https://m-naoki-m.hashnode.dev/7-ways-a-markdown-table-silently-breaks-and-the-60-second-checklist</guid><category><![CDATA[markdown]]></category><category><![CDATA[webdev]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Naoki M]]></dc:creator><pubDate>Sun, 12 Jul 2026 03:41:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a4f8e7c2cccfb7aa70a7424/8f3e36f8-ceb3-4a23-96df-4026f68d7d42.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Markdown tables have no error messages. When one breaks, the renderer does not tell you why — it just dumps your pipes and dashes as plain text, or quietly eats a column. I run a browser-side converter site (<a href="https://formatarc.com">FormatArc</a>), and broken tables are one of the most common reasons people reach for a converter in the first place.</p>
<p>Here is the checklist I actually use, in the order that catches the most cases first.</p>
<h2>1. The delimiter row does not match the header</h2>
<p>Every GFM table needs a delimiter row (<code>| --- | --- |</code>) with <strong>the same number of cells as the header row</strong>. If the counts differ, <a href="https://github.github.com/gfm/#tables-extension-">the spec says the table is not recognized at all</a> — which is why you get raw text instead of a slightly-wrong table.</p>
<pre><code class="language-markdown">| Name | Role |          &lt;- 2 columns
| --- | --- | --- |      &lt;- 3 columns: whole table renders as text
</code></pre>
<p>Count the pipes in both rows first. This one mistake explains most "my table renders as plain text" cases.</p>
<p>By the way: the delimiter cells only need <strong>one hyphen each</strong> per the spec. The "you need at least three dashes" advice you see in old forum answers is folklore — three hyphens are fine for readability, but not required.</p>
<h2>2. No blank line before the table</h2>
<p>GFM tables interrupt a paragraph in some renderers but not in others. The portable fix is boring: always leave one empty line between your text and the table.</p>
<h2>3. A raw pipe inside a cell</h2>
<p>A literal <code>|</code> inside cell content splits the cell. Escape it as <code>\|</code>:</p>
<pre><code class="language-markdown">| Command | Meaning |
| --- | --- |
| a \| b | pipe a into b |
</code></pre>
<h2>4. Code spans do not protect pipes</h2>
<p>This one surprises people: wrapping a pipe in backticks does <strong>not</strong> stop it from splitting the cell. <a href="https://github.github.com/gfm/#tables-extension-">Cell boundaries are parsed before inline code</a>, so <code>`a | b`</code> still breaks into two cells. You need <code>\|</code> even inside backticks.</p>
<h2>5. Line breaks inside a cell</h2>
<p>A literal newline ends the row — there is no multi-line cell in GFM. Use <code>&lt;br&gt;</code> where the platform allows inline HTML, or restructure the data so each row fits on one line.</p>
<h2>6. Your renderer does not do tables at all</h2>
<p>Tables are a <strong>GFM extension, not core Markdown</strong>. The <a href="https://spec.commonmark.org/">CommonMark spec</a> has no table syntax, so a strict CommonMark renderer (some static site generators, comment systems, and chat apps) will show your pipes as plain text no matter how correct they are. Check what dialect your platform renders before debugging your syntax.</p>
<h2>7. Invisible characters from copy-paste</h2>
<p>Tables assembled from copied content sometimes carry full-width pipes (<code>｜</code> from CJK input methods), non-breaking spaces, or smart quotes. They look identical on screen and break the parse. Retype the pipes if a table refuses to work and everything else checks out.</p>
<h2>When fixing is slower than rebuilding</h2>
<p>If a table came from a spreadsheet, a CSV export, or an API response, hand-repairing pipes is usually slower than regenerating the table. I built <a href="https://formatarc.com/en/csv-to-markdown/">a CSV-to-Markdown-table converter</a> for exactly this: paste CSV, get a correctly aligned GFM table. It runs 100% in your browser — nothing is uploaded, which matters when the data is an internal export.</p>
<p>Full disclosure: FormatArc is my own project. The checklist above works regardless of what tool you rebuild with.</p>
<h2>The 60-second version</h2>
<ol>
<li>Header and delimiter row: same cell count?</li>
<li>Blank line before the table?</li>
<li>Raw <code>|</code> in a cell → <code>\|</code></li>
<li>Pipes in code spans → still <code>\|</code></li>
<li>Newline in a cell → <code>&lt;br&gt;</code> or restructure</li>
<li>Does your renderer support GFM tables at all?</li>
<li>Full-width <code>｜</code> or invisible characters from copy-paste?</li>
</ol>
<p>If all seven pass and it still breaks, the problem is almost always the platform, not your Markdown.</p>
]]></content:encoded></item></channel></rss>