Sashmark / Blog

Markdown code blocks: fences, languages and backticks

Bobby Huang ·

A Markdown code block is text set apart as code. Open it with a fence, which is a line of three backticks, and close it with a matching fence after the code. Tildes can replace the backticks. A word after the opening fence, like bash or python, names the language. The spec does not say what a viewer does with that word, so colors depend on the viewer. To show backtick lines inside a block, wrap it in a longer fence, such as four backticks. For code inside a sentence, use single backticks.

Key takeaways

  • A fenced block opens with three or more backticks or tildes and closes with a line of the same character, at least as long.
  • The word after the opening fence names the language. Whether it gets colors is up to the viewer.
  • To show a fence inside a block, make the outer fence longer, or switch to tildes.
  • A fence with no closing line runs until the file ends, or until the list item or quote holding it ends.
  • Rendered code blocks are on the planned v1 list for Sashmark, which is in development and has not been released.

Agent-written Markdown can hold commands to run and config to paste, and those sit in code blocks. An unclosed fence can turn the rest of the page into code.

A bash block wrapped in four backticks sits in the code section of the Markdown syntax guide. That outer fence is there so the inner one shows as text, and the reason is a rule about fence length.

Write a fenced code block

A fence is a line of at least three backticks. One opens the block and a second one closes it.

```
npm run build
npm test
```

Many viewers show this as a box of monospace text. The text inside keeps its spacing and line breaks.

Tildes can stand in for backticks:

~~~
npm run build
npm test
~~~

The result is the same code block. You can't mix the two in one fence. A block opened with backticks must close with backticks.

Two small rules help here. The opening fence can sit up to three spaces in from the margin. Four spaces is too many, and the line turns into a different kind of block. Also, Markdown inside a code block is not parsed. A # or a * in there stays a plain character, not a heading or emphasis.

Name the language

Put a word right after the opening fence. The text after the fence is called the info string.

```python
def greet(name):
    return "Hello, " + name
```

Many viewers read the first word and color the code to match Python. Others ignore the word. The CommonMark spec says the first word is typically used for the language, but it leaves the treatment to the tool. So the colors you see depend on the viewer.

Words you will see there include bash, python, json, yaml, javascript and markdown. Use the short name your viewers know. Any word there still leaves the file valid Markdown. Some viewers just leave it uncolored.

One limit: after a backtick fence, the info string can't contain a backtick.

Show backticks inside a code block

Say you want to show someone how a code block looks in the raw file. The bare three-backtick line inside would close your block early. The fix is a longer outer fence.

A block only closes on a fence of the same character that is at least as long as the opening one. So open with four backticks, and three-backtick lines inside are just text:

````markdown
```bash
pnpm test
```
````

The output is one code block holding all three inner lines, backticks included.

The example above is itself wrapped in tildes. That is the second way out. A tilde fence ignores backtick lines, so it can hold any number of them.

Indented code blocks

The second kind of block has no fences. Each of its lines starts with four spaces.

Run these two commands:

    npm run build
    npm test

Those indented lines become a code block, with the four spaces removed.

Indented blocks have two catches:

  • After a paragraph, they need a blank line. An indented line right under a paragraph just joins that paragraph. Without the blank line, you get no code block.
  • There's no info string. So there's no spot for a language word either.

They also clash with lists, where indented lines can belong to a list item. If a line could be either, the list reading wins. Fenced blocks avoid all of this, so use them in new files.

Inline code spans

Inside a sentence, code goes between single backticks.

Run `npm test` before you push.

Many viewers show "npm test" in monospace, right in the line.

To show a backtick inside inline code, wrap it in two backticks and add a space on each side:

The fence is `` ``` `` on its own line.

The result is a code span holding three backticks. The single space at each end is stripped.

When a fence never closes

If a block has no closing fence, it runs on until the file ends. When the fence is nested in a quote or list item, the code stops where that item or quote ends instead. At the top level, every heading, list and link below the opening line becomes part of one long code block.

Agent output can end up this way. An agent may stop partway through a reply, or trim a file and drop the last fence. Here is what that looks like in the raw text:

## Step 2

```bash
pnpm test

## Step 3

Check the logs and report back.

Step 3 is never parsed as a heading. It sits inside the bash block with everything after it. The fix is one line: add the closing fence right after pnpm test.

One stray fence near the top of a file is enough to swallow a long plan. If much of a page looks like code, look for the last fence that opened above the point where the trouble starts.

Check the fences in an agent-written file

These steps work in a macOS or Linux terminal, and they slot into a wider review of agent-written Markdown. Swap notes.md for your file. The commands only look at fences up to three spaces in, so a fence nested deeper in a list or a quote can slip past them.

  1. Count the fence lines. An odd count means a fence may be open, so look closer. Treat the count as a hint.

    grep -cE '^ {0,3}(```|~~~)' notes.md
    
  2. List them with line numbers. Pair them off from the top. The one with no partner is the likely problem.

    grep -nE '^ {0,3}(```|~~~)' notes.md
    
  3. Look at the line after the last fence. If prose follows a fence that should have closed, add the closing line there.

  4. Check for nested fences. If a block shows other fences inside it, its outer fence must be longer, or use tildes. The count in step 1 is rough for these files, so read them by eye.

  5. Check the language words. Make sure each opening fence that needs one has a short, plain name like bash or json.

  6. Render the file and scroll to the end. Check that the last headings are not stuck inside a code block. If they are, recheck the fence above them.

Where Sashmark fits

Sashmark is a desktop app for reading and editing the Markdown files that AI agents produce. It is in development and has not been released. The app itself has no AI features. The v1 plan includes rendering code blocks, headings and block quotes on one clean page, which should make a broken fence easy to spot. See the planned features, the help pages and the roadmap for where each part stands.

You can see what Sashmark is and follow along at sashmark.com.

Questions

Can I get syntax highlighting in a Markdown code block?
Type the language name right after the opening fence, with no space needed, like ```python. Many viewers use that word to pick colors. Some show no colors at all. The file is still correct either way.
How do I show triple backticks inside a code block?
Make the outer fence longer than any fence inside it. Four backticks around a block that holds three-backtick lines works. A tilde fence also works, since a tilde fence only closes on tildes.
Should I use fenced or indented code blocks?
Use fenced blocks. They can name a language, they do not need a blank line before them, and the start and end are easy to spot. Indented blocks are easy to break by accident.

What Sashmark plans for v1: Features · Roadmap

View as Markdown