Fenced Code Blocks: The One Markdown Feature That Makes Documentation Readable
Fenced code blocks with language info strings turn gray monospace slabs into highlighted, copy-safe code — and the raw Markdown stays readable in any plain-text viewer. Here's how to use them well, including the nesting trick and the portability limits.
23 Sept 2025, 17:13 UTC

You paste a Python snippet into a README. It renders as a gray slab of monospace text, indistinguishable from shell commands, JSON, and log output. Readers squint, copy-paste breaks, and the docs feel unfinished. The fix is one of Markdown's most quietly useful features: the fenced code block with a language info string.
The takeaway up front: wrap code in triple backticks, put the language name on the opening fence, and every mainstream renderer will highlight it — while the raw source stays perfectly readable in a plain-text editor. That second property is the feature's real superpower.
Why indented code blocks fell out of favor
Original Markdown only had indented code blocks: start every line with four spaces. It works, but it has practical problems. Inside a list item you need eight spaces, which editors mangle. The block's boundaries are implicit, so a stray blank line or a dedented line silently ends the block. And there's nowhere to say what language this is, so no renderer can highlight it.
Fenced code blocks — standardized in CommonMark and popularized by GitHub Flavored Markdown — solve all three. An opening fence of three or more backticks starts the block, a matching closing fence ends it, and the content between is literal. No escaping of *, _, or # inside is ever needed.
The info string: one word that does the work
The text after the opening fence is called the info string. By convention — and this is the part that matters — its first word names the language. GitHub, GitLab, and most static site generators feed that word to their syntax highlighter:
```python
def retry(attempts=3):
for i in range(attempts):
yield i
```That renders with Python-aware highlighting on any GFM-compatible platform. A few rules worth internalizing:
- The closing fence must be at least as long as the opening one. Three backticks open, three or more close.
- Anything after the first word of the info string is not standardized. Some renderers interpret extra words (titles, line-number flags, highlight ranges), but each does it differently. Treat extra words as renderer-specific hints, never as portable Markdown.
- If the language tag is misspelled or unknown to the highlighter, the block silently falls back to plain monospace. No error, no warning — you have to look at the rendered output.
A worked example: README install section
A typical README section mixing two languages, written once and rendered well everywhere:
## Install
```bash
pip install widgetlib
widgetlib init
```
Then add the config:
```json
{
"endpoint": "https://api.example.com",
"retries": 3
}
```On GitHub this highlights the shell commands and the JSON differently. In a terminal with cat README.md, it's still clean, indented, copy-pasteable text. No HTML, no plugins, no build step required for the source to be useful.
Nesting: when your code contains code fences
If you're documenting Markdown itself — say, showing a reader what a fenced block looks like — a triple-backtick fence can't contain a line of triple backticks; it would close early. Two escapes exist:
- Use a longer outer fence: four backticks can wrap content containing three.
- Use tilde fences (
~~~) for one level and backticks for the other.
Both are valid CommonMark. My recommendation: standardize on backticks project-wide and reach for four-backtick fences when nesting. Mixing tildes and backticks across files adds inconsistency for zero benefit.
Honest limitations
Markdown has no standard for line numbers, diff highlighting (+/- lines), or file captions on code blocks. Renderers bolt these on through info-string extensions, but they're not portable — a block that shows line numbers in one static site generator renders as plain text in another. If you need those features, check your specific toolchain's docs and accept the lock-in.
Also remember that highlighting quality depends entirely on the renderer's highlighter knowing your language tag. Common tags (python, bash, json, yaml, go) are safe bets everywhere; obscure ones are not.
Verify before you commit
Two quick checks, no tooling required:
- Paste your fenced block into a GitHub comment preview (or your generator's local preview) and confirm the highlighting actually appears — this catches misspelled language tags.
- If you nested fences, confirm the inner backticks render as literal text rather than closing the block early.
Fences plus a language tag cost you eleven characters and buy you highlighted, copy-safe, plain-text-readable code in every doc you write. There's no cheaper upgrade in Markdown.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.