---
title: "How to underline text in Markdown: pandoc test results"
slug: markdown-underline-text
excerpt: "How to underline text in markdown with pandoc: under Bracketed spans with an underline class, you'll see the reader settings that gave a u element."
quickAnswer: "Under Underscores in plain CommonMark, you'll find a spec search and pandoc output for one and two underscores. The section Bracketed spans with an underline class shows which pandoc readers made a u element from a span in this test. Under Raw HTML u and ins tags, you'll see pandoc output for hand-typed u and ins tags. Test lines where pandoc kept the brackets as text are collected under When the brackets stay as text. The section Test a line for underline output walks through numbered steps for your own files."
keyTakeaways:
  - "Spec search counts and the underscore test sit under Underscores in plain CommonMark."
  - "Reader settings that gave a u element from a span are listed under Bracketed spans with an underline class."
  - "Output for hand-typed u and ins tags is shown in the section Raw HTML u and ins tags."
  - "Escaped, spaced and code-span versions of the span are tested under When the brackets stay as text."
  - "A span wrapped in bold markers gets its own test in the section Underline inside bold."
publishedDate: 2026-10-20
dateModified: 2026-10-20
author: Bobby Huang
cluster: B
role: post
status: published
faq:
  - q: Does CommonMark have an underline syntax?
    a: "For spec search counts plus pandoc underscore output, see the section Underscores in plain CommonMark."
  - q: How do I underline text with pandoc?
    a: "Pandoc runs on a span with an underline class are in the section Bracketed spans with an underline class."
  - q: Can I use a u tag instead of Markdown syntax?
    a: "Pandoc output for typed u and ins tags is shown under Raw HTML u and ins tags."
  - q: Why are my square brackets still in the output?
    a: "Under When the brackets stay as text, you'll find the test lines where pandoc printed the brackets as written."
related:
  - markdown-highlight-text
  - raw-html-in-markdown
  - markdown-bold-italic
---

You put underscores around a name to underline it, and pandoc handed back an `<em>` element. Two underscores gave you `<strong>`. Neither run produced an underline element in this test.

Here's how to underline text in Markdown using what pandoc 3.8.3 accepted on one machine: a bracketed span with an `underline` class, and the raw HTML `<u>` and `<ins>` tags. The outputs quoted here came from those tests.

## Underscores in plain CommonMark

Underlining gets no syntax of its own in CommonMark. A case-insensitive grep of spec release 0.31.2 for "underline" matched 10 lines, and each one belongs to the text on setext heading underlines (lines 996 to 1598, plus line 8672). Searches for `<u>` and `<ins>` in the spec matched 0 lines each.

In the spec, underscores belong to emphasis. Lines 5771 to 5773 say emphasis starts and ends with the same character, `_` or `*`, and lines 5781 to 5784 say the same for strong emphasis.

The test file `ul-emph.md` held one line:

```markdown
_Jane Doe_ and __Doe Holdings__
```

On that file, `pandoc -f commonmark -t html ul-emph.md` put `<em>` around the name and `<strong>` around the company, both inside one paragraph. Switching to the `markdown` reader returned an identical line. A single underscore per side made `<em>`, a pair made `<strong>`, and no `<u>` element appeared. The [guide to bold and italic markers](markdown-bold-italic.md) lays out the star and underscore rules in full.

This test also searched pandoc's extension lists for `commonmark` and `markdown` for the word underline. No extension name matched.

## Bracketed spans with an underline class

Pandoc calls the bracket form the `bracketed_spans` extension. The text goes between square brackets, and a class sits in curly braces straight after them. Pandoc's extension lists, printed during this test, showed `+bracketed_spans` under the `markdown` reader and `-bracketed_spans` under `commonmark`. That means `markdown` has the extension on from the start and `commonmark` has it off.

The test file `ul-span.md`:

```markdown
[Jane Doe]{.underline}
```

Three reader settings ran on it:

- `pandoc -f commonmark -t html ul-span.md` returned `<p>[Jane Doe]{.underline}</p>`. The brackets and the braces came through as plain text.
- `pandoc -f commonmark+bracketed_spans -t html ul-span.md` returned `<p><u>Jane Doe</u></p>`.
- `pandoc -f markdown -t html ul-span.md` gave the identical `<u>` output without any extension flag.

The span behaved the same way mid-sentence. The file `ul-x-inline.md` held this line:

```markdown
Jane Doe reads the [Doe Holdings]{.underline} memo today.
```

Both `commonmark+bracketed_spans` and `markdown` returned this line: `<p>Jane Doe reads the <u>Doe Holdings</u> memo today.</p>`. Plain `commonmark`, lacking the extension, gave the sentence back as typed, wrapped in a paragraph.

A shorter class name split the two readers. For `[Jane Doe]{.ul}`, the `markdown` reader returned `<p><u>Jane Doe</u></p>`. The `commonmark+bracketed_spans` setting returned `<p><span class="ul">Jane Doe</span></p>` instead. With the full `.underline` class, both settings produced a `<u>` element.

## Raw HTML u and ins tags

Another route skips Markdown syntax: type the tag yourself. Section 6.6 of the spec (lines 8177 to 8182) treats text inside angle brackets that looks like a tag as raw HTML and sends it to the output unescaped. That passage also says the tag names it accepts include ones outside current HTML.

The test file `ul-html.md`:

```html
<u>Jane Doe</u>
```

The two readers, `commonmark` and `markdown`, each returned `<p><u>Jane Doe</u></p>`, with the tag kept as typed.

The test file `ul-ins.md` swapped in the other tag:

```html
<ins>Jane Doe</ins>
```

From the `commonmark` reader, the output was `<p><ins>Jane Doe</ins></p>`. From the `markdown` reader, it was three lines with no paragraph tags:

```text
<ins>
Jane Doe
</ins>
```

With the tag placed mid-sentence, as in `ul-x-ins-inline.md`, both readers kept it inside one paragraph: `<p>Jane Doe reads the <ins>Doe Holdings</ins> memo today.</p>`. The `<u>` version of that sentence, `ul-x-html-inline.md`, came out the same way under both readers, with `<u>` where `<ins>` had been.

These typed tags were the one form tested here that put a `<u>` or `<ins>` element in the output of plain `commonmark` with no flag. For what can happen to raw HTML once pandoc is done, the [raw HTML walkthrough](raw-html-in-markdown.md) explains block-level HTML and why blank lines matter.

## When the brackets stay as text

A few test lines held on to their brackets and braces even with a reader that knows the span. The extension-off case reuses `ul-x-inline.md`; the other three had test files of their own.

- **Backslashes in front of the brackets.** For `\[Jane Doe\]{.underline}`, both `markdown` and `commonmark+bracketed_spans` returned `<p>[Jane Doe]{.underline}</p>`. Per spec line 481, any ASCII punctuation character may take a backslash, and lines 332 to 337 list square brackets as ASCII punctuation. For more on escapes, read the [post on backslash escapes](markdown-escape-characters.md).
- **A space before the curly brace.** For `[Jane Doe] {.underline}`, both settings gave the line back as typed, space and all.
- **Backticks around the span.** `ul-x-code.md` held the span between backticks, and pandoc returned `<code>[Jane Doe]{.underline}</code>` under `commonmark` and `markdown`.
- **With `-bracketed_spans` on the reader.** From `pandoc -f markdown-bracketed_spans -t html ul-x-inline.md`, it produced `<p>Jane Doe reads the [Doe Holdings] memo today.</p>`. The brackets stayed, the curly-brace part was gone from the output, and no `<u>` element came out.

Here is the code span line from `ul-x-code.md`:

```markdown
See `[Jane Doe]{.underline}` here.
```

## Underline inside bold

One more test file wrapped the span in double stars:

```markdown
**[Jane Doe]{.underline}**
```

Under `markdown` and `commonmark+bracketed_spans`, pandoc returned `<p><strong><u>Jane Doe</u></strong></p>`. The `<u>` element sat inside `<strong>`, matching the order of the markers in the file. Only this one order was tested.

## Test a line for underline output

If an underline doesn't show up, copy that line out on its own.

1. Save just that line in a new test file.
2. Feed that file to `pandoc -f commonmark -t html`. This test ran the same command on `ul-span.md`. If pandoc prints the brackets and braces back, you got the same result plain `commonmark` gave here.
3. Repeat the run with `-f markdown`. To stay on the `commonmark` reader, use `-f commonmark+bracketed_spans` instead.
4. Send the output through grep. The `-c` flag prints the number of matching lines. Here, `pandoc -f markdown -t html ul-span.md | grep -c '<u>'` printed `1`. The same pipe with `pandoc -f commonmark -t html ul-span.md | grep -c '<u>'` printed `0`.
5. If grep prints 0, look at the cases under When the brackets stay as text: a space before the brace, backslashes before the brackets, or backticks around the span.
6. To keep a plain `commonmark` run, swap the span for a typed `<u>` tag, repeat step 2 and look for the tag in the output.

## Where Sashmark fits

Plans for Sashmark v1 cover fixing words right where they sit in the rendered view. Also in the plan, a clean Round-Trip would hold untouched lines to their exact original bytes. The v1 plan also includes a Changes View, planned to flag what changed in a file after your last read of it, with no git needed. It is in development and has not been released. The app itself has no AI features. See the [planned features](/features), the [help pages](/help) and the [roadmap](/roadmap) for where each part stands.

Progress notes: [sashmark.com](https://www.sashmark.com/).
