Skip to content

Markdown: a Liquid tag alone on its line is reflowed into the neighbouring paragraph with proseWrap: always #19985

Closed

Description

@pepone

Prettier 3.9.6
Playground link

# Options (if any):
--parser markdown
--prose-wrap always
--print-width 120

Input:

{% callout type="info" %}
For the sake of example, the router's public address is 5.6.7.8 and its private address is 10.0.0.1.
{% /callout %}

Output:

{% callout type="info" %} For the sake of example, the router's public address is 5.6.7.8 and its private address is
10.0.0.1. {% /callout %}

Expected output:

{% callout type="info" %}
For the sake of example, the router's public address is 5.6.7.8 and its private address is
10.0.0.1.
{% /callout %}

Why?

Prettier has understood {% ... %} Liquid tags in Markdown since 1.13 (#3695): it parses them as an inline
liquidNode and never edits their contents. But it has no block form of that node. A tag that stands alone on a line
is, to the paragraph printer, the first word of the paragraph next to it, and proseWrap: always (and never) reflows
the two onto shared lines.

In every language that uses this syntax — Liquid in Jekyll ({% highlight %}, {% include %}, {% raw %}), Markdoc
({% callout %}, {% if %}), Twig — a tag alone on a line is a block tag and a tag inside a line of text is an inline
tag. The reflow changes which one it is. Markdoc, for example, renders the output above as an inline tag inside a <p>,
which puts an <aside> inside a paragraph. Two tags on adjacent lines fare worse
(playground):

We write the server first.
{% /if %}
{% if lang == "js" %}

becomes

We write the server first. {% /if %}
{% if lang == "js" %}

where the closing tag is now inline and the opening one is still a block, so they no longer pair, and Markdoc drops the
section. Because the result is Prettier's own output, prettier --check passes on it: the damage is silent in CI.

proseWrap: preserve (the default) is unaffected, since it keeps the author's line breaks. The problem appears exactly
when a project turns on wrapping, which is when people run --check in CI and rely on it.

Suggestion: treat a Liquid tag that occupies a whole line (leading whitespace allowed, nothing else on the line) as a
block-level node, the way a block html node is handled: print it verbatim, never fill it into a neighbouring
paragraph, and keep whether a blank line separated it from its neighbours. Inline tags, the current behaviour, are
untouched.

Workaround: a parser plugin that spreads the built-in Markdown parser and adds a preprocess step which puts a blank
line on each side of every standalone tag line (outside code fences) makes Prettier see each tag line as its own
paragraph. About forty lines; happy to share it or to turn the suggestion above into a PR if the maintainers want it.

Activity

  1. devtechedge commented on Sep 9, 2026

    @devtechedge

    Working on a focused fix so a Liquid/Markdoc tag that stands alone on its line stays a block boundary under proseWrap: always / never, instead of being reflowed into the neighbouring paragraph. Will add regression fixtures and open a PR linking this issue.

  2. devtechedge commented on Sep 9, 2026

    @devtechedge

    Stepping back - on current main the reported inputs already keep standalone Liquid tags as block boundaries under proseWrap always/never (AST is root liquidNode + paragraph + liquidNode). Likely fixed since the report; happy to reopen if there is a remaining repro.

  3. added theissue type on Sep 10, 2026
  4. added a commit that references this issue on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions