DEV Community

frank
frank

Posted on

Why One Newline Renders Differently Across Markdown Tools

Press Enter once in a Markdown editor and the preview may keep the text in one paragraph. Move the same file to a chat app and every source line may become a visible line break.

That is usually not a parser bug. Three separate things are being confused:

  1. the file's line ending (LF, CRLF, or CR),
  2. the Markdown node (softbreak or hard line break), and
  3. the browser's final layout.

I tested the cases below on August 14, 2026 with fixed versions:

  • commonmark.js 0.31.2
  • markdown-it 15.0.0
  • Marked 18.0.9
  • CommonMark specification 0.31.2

One Enter is a soft break by default

Input:

alpha
beta
Enter fullscreen mode Exit fullscreen mode

All three parsers produced one paragraph in their default configuration:

<p>alpha
beta</p>
Enter fullscreen mode Exit fullscreen mode

CommonMark calls the line ending a soft break. A renderer may emit it as a newline character or a space. Under the browser's usual white-space: normal, that whitespace is collapsed, so the two words normally appear on one visual line unless the container wraps them.

This distinction matters: a newline in generated HTML source is not necessarily a visible line break.

Two spaces and a backslash create hard breaks

CommonMark supports two forms:

alpha  
beta
Enter fullscreen mode Exit fullscreen mode
alpha\
beta
Enter fullscreen mode Exit fullscreen mode

Both produced a hard-break node and a <br> element in all three parsers:

<p>alpha<br>
beta</p>
Enter fullscreen mode Exit fullscreen mode

I prefer the backslash form when a break is semantically important. It is visible during review, while trailing spaces are easy for an editor or formatter to delete.

LF, CRLF, and CR normalized to the same result

I rendered these inputs separately:

alpha\nbeta
alpha\r\nbeta
alpha\rbeta
Enter fullscreen mode Exit fullscreen mode

All three parsers treated them as the same paragraph with a soft break by default. Converting a repository from CRLF to LF should therefore not change this Markdown meaning by itself.

Trailing-space cleanup is a different story. Removing the second space from a hard break changes the document structure.

The breaks option changes platform behavior

With breaks: true, markdown-it and Marked converted a normal paragraph newline to <br>:

Configuration Result for alpha\nbeta
commonmark.js default soft break
markdown-it default soft break
markdown-it breaks: true <br>
Marked default soft break
Marked breaks: true <br>

That option explains many "works here, breaks there" reports. A chat renderer may intentionally preserve author-entered lines, while a documentation system follows CommonMark's paragraph behavior.

Treat the option as part of the platform's Markdown dialect. Recording only "we use Markdown" is not enough for a reproducible publishing pipeline.

Edge cases expose the real boundary

One trailing space is still soft

alpha 
beta
Enter fullscreen mode Exit fullscreen mode

It does not create a hard break. The parsers differed slightly in the exact HTML whitespace they retained, but not in the resulting Markdown structure.

Two spaces at the end of a block do not create <br>

alpha  

next
Enter fullscreen mode Exit fullscreen mode

CommonMark says a hard break separates inline content inside a block. It cannot occur at the end of the paragraph. commonmark.js and markdown-it removed the trailing spaces. Marked 18.0.9 retained them in the HTML string, but none of the three emitted <br>.

The page looks the same in a browser, yet byte-for-byte HTML snapshots differ. Decide whether your tests protect semantic structure or exact serialization.

Code spans use different whitespace rules

`alpha
beta`
Enter fullscreen mode Exit fullscreen mode

All three produced:

<p><code>alpha beta</code></p>
Enter fullscreen mode Exit fullscreen mode

The newline is normalized inside the code span. A backslash or two trailing spaces there do not become a hard break.

A practical testing strategy

For a multi-output documentation pipeline, test three layers:

  1. preserve the exact fixture, including visible line-ending and trailing-space notation;
  2. compare the parsed structure, especially soft-break and hard-break nodes;
  3. compare the target output and inspect it under the real CSS.

If the final target is fixed-layout output, I also check a representative file against the Markdown-to-PDF formatting workflow. That does not replace parser tests; it catches the separate class of layout differences introduced after parsing.

My working rule is simple: use blank lines for paragraphs, a visible backslash for intentional inline breaks, and automatic wrapping for prose. Then lock parser versions and options wherever the same source must render consistently.

Who should own line-break semantics in a shared document system: the author in the source, or the rendering environment for each destination?

Top comments (0)