Markdown Code Blocks: Syntax Highlighting in 150+ Languages
How to write Markdown code blocks — fenced and indented, with syntax highlighting, line numbers, inline code, escape sequences, and tips for technical writing.
If you're writing for a developer audience, code blocks carry half the message. The good news: Markdown supports them out of the box. The better news: with one extra hint, you get syntax highlighting in 150+ languages — for free.
This guide covers everything you need to know about Markdown code blocks: fenced vs. indented, language hints, inline code, edge cases, and the patterns the best technical writers use.
The three ways to write code in Markdown
Markdown has three code styles, each suited to a different situation:
- Inline code — for short snippets within a sentence
- Indented code blocks — for multi-line code in plain CommonMark
- Fenced code blocks — the modern, language-aware way
You'll use fenced blocks 95% of the time.
Inline code
Wrap inline code with single backticks:
Run `npm install` to install dependencies.
For text that contains a backtick (like the bash `command` substitution), use double backticks:
Use ``code with `backticks` inside`` for tricky snippets.
If your inline code starts or ends with a backtick, add a space inside the wrapper:
`` ` `` is the backtick character.
Fenced code blocks
The fenced form is what every modern Markdown renderer supports. It's three backticks on a line by themselves:
```
function greet(name) {
return `Hello, ${name}!`;
}
```
Renders as:
function greet(name) {
return `Hello, ${name}!`;
}
The opening fence and closing fence don't need to be on lines by themselves in the strict sense, but the convention is one fence per line for readability.
Language hints for syntax highlighting
Add a language identifier immediately after the opening fence:
```ts
const greet = (name: string) => `Hello, ${name}!`;
```
Renders with TypeScript-specific colors:
const greet = (name: string) => `Hello, ${name}!`;
Most renderers use highlight.js, which supports 150+ languages. Common ones:
| Language | Hint |
|---|---|
| JavaScript | js or javascript |
| TypeScript | ts or typescript |
| Python | py or python |
| Shell / bash | sh, bash, shell |
| HTML | html |
| CSS / SCSS | css or scss |
| JSON | json |
| YAML | yaml or yml |
| SQL | sql |
| Markdown | md or markdown |
| Rust | rs or rust |
| Go | go |
| Java | java |
| C / C++ | c / cpp |
| Diff | diff |
| Mermaid | mermaid (renders as a diagram) |
If you're unsure, leave the hint off — most highlighters will autodetect the language with reasonable accuracy.
Diff blocks for showing changes
The diff language hint gives you red/green coloring for additions and removals:
```diff
- const greet = (name) => `Hello, ${name}!`;
+ const greet = (name: string) => `Hello, ${name}!`;
```
Use it in PR descriptions, changelogs, and tutorials where you want to highlight what changed.
Mermaid diagrams in fenced blocks
When you use ```mermaid, our preview renders the code as an actual diagram:
```mermaid
flowchart LR
Idea --> Draft --> Publish
```
This works in Markdown Viewer, GitHub, GitLab, and Notion (with extensions). For a full guide, see our Markdown to PDF post which covers Mermaid in printable output.
Indented code blocks (the old way)
Before fenced blocks, the only way to write a code block was to indent every line by four spaces:
Normal paragraph.
function greet(name) {
return "Hello, " + name;
}
Another paragraph.
This still works in strict CommonMark, but it has serious drawbacks:
- No language hints — no syntax highlighting.
- Easy to break — change the indent and the block silently becomes prose.
- Hard to copy-paste — every line has unwanted leading whitespace.
Use fenced blocks. The indented form is mostly a historical artifact.
Showing fenced blocks inside Markdown
This is the puzzle that confuses every writer the first time: how do you show ```ts literally without it becoming a code block?
The answer: use more backticks on the outer fence than on the inner one. Four backticks outside, three inside:
````md
```ts
const x = 1;
```
````
Add a language hint of md to the outer fence so the highlighter knows it's a Markdown example.
Escaping inside code blocks
Inside a code block, nothing is parsed — no Markdown, no HTML. That means you can paste:
```html
<script>alert('safe inside a code block')</script>
```
…and it shows up as literal text, not as a script. This is why Markdown is safe for displaying code samples even when they look dangerous.
Long lines and word wrap
Code blocks don't wrap by default. Long lines cause a horizontal scrollbar in the preview, which is usually what developers want — code shouldn't reflow.
If your audience prefers wrapping (helpful in narrow embeds), CSS can override the behavior:
pre {
white-space: pre-wrap;
word-break: break-word;
}
But: wrapped code is hard to copy cleanly. Most renderers default to scrolling for a reason.
Adding line numbers
Plain Markdown doesn't support line numbers. Three workarounds:
1. Use a renderer that adds them automatically
MkDocs Material, Docusaurus, and many docs themes ship a line-number plugin. Configure it once and every code block gets gutter numbers.
2. Use the linenos attribute (Pandoc-style)
```ts {linenos=true}
const x = 1;
const y = 2;
Works in MkDocs Material, Hugo, and a handful of other tools. Not portable.
### 3. Highlight specific lines
Even better than numbers: highlight the lines that matter. The syntax varies, but the convention is `{hl_lines=2-4}` after the language:
```md
```ts {3,5-7}
function buildGreeting(name: string) {
// Not highlighted
if (!name) { // highlighted
name = "stranger";
}
return `Hello, ${name}!`; // highlighted
// … etc
}
Markdown Viewer doesn't currently highlight specific lines, but the language hint and syntax are still preserved.
## Showing terminal commands and output
For shell sessions, use the `bash` (or `console`) hint and prefix prompt lines with `$`:
````md
```bash
$ npm install
$ npm run dev
Some renderers know to skip the `$` when copying. For mixed prompts and output, the `console` language is a good convention:
````md
```console
$ ls
README.md package.json src/
$ pwd
/home/user/project
```
This makes it obvious to the reader which lines are commands and which are output.
Inline code with language hints (kind of)
You can't add a language hint to inline code in standard Markdown, but you can simulate it:
Run `npm install` and then `npm run dev` to start.
If you really need highlighted inline code, render to HTML and use a snippet of CSS to color specific token classes. It's rarely worth it.
Common code block mistakes
1. Missing the closing fence. If you forget the closing ```, every paragraph below becomes part of the code block — until the next fence.
2. Mixing tabs and spaces inside the block. This doesn't break the block, but it makes the rendered code ugly.
3. Wrong language hint. Misspelling the hint silently falls back to plain text. typescritp won't highlight anything.
4. Indenting the fence. A fenced block must start at the left edge (or be properly indented inside a list item). Off-indented fences are treated as paragraphs.
5. Using inline code for multi-line content. multi\nline becomes a single line. Use a fenced block instead.
Code blocks in tables (sort of)
Markdown tables don't accept fenced code blocks inside cells. For short snippets, use inline code:
| Command | Description |
| ------------- | --------------------- |
| `npm install` | Install dependencies |
| `npm run dev` | Start the dev server |
For multi-line code, move it outside the table and link from the cell.
Best practices for writing code blocks
After years of writing technical documentation, a few patterns hold up:
- Always add a language hint. Even for "obvious" snippets like JSON.
- Keep blocks short. 10–20 lines max. Long blocks get skipped.
- Annotate with comments, not surrounding prose. Readers focus inside the block.
- Test your code. A broken example is worse than no example.
- Match the style of the surrounding ecosystem. TypeScript readers expect
const, semicolons, and Prettier-like formatting.
Try it in the editor
Open the Markdown editor and paste a few code blocks. The preview highlights them automatically based on the language hint. The status bar shows how many words your code blocks contribute to your total — we exclude them for an accurate word count.
Related articles
- Markdown cheat sheet — every Markdown feature on a single page
- Markdown syntax guide — CommonMark and GFM, end to end
- Markdown table guide — including code in cells
Quick reference
`inline code` Inline snippet
``inline `with` backticks`` Inline with backticks
``` Fenced block (auto)
code
```
```ts Fenced with language
const x: number = 1;
```
```diff Diff block
- old
+ new
```
```mermaid Mermaid diagram
graph LR
A --> B
```
That's everything you need. With the language hint and a fenced block, your readers (and search engines) instantly know what they're looking at.
Written by Markdown Viewer Team. Found this useful? Try the editor →
Keep reading
Hand-picked articles related to this one.
A complete guide to adding images in Markdown: basic syntax, resizing with HTML, alignment, alt text best practices, and hosting options for GitHub READMEs, blogs, and docs.
Everything you need to know about Markdown tables — syntax, alignment, escape sequences, GFM extensions, accessibility tips, and how to generate them faster.
A complete Markdown syntax guide. Covers CommonMark, GitHub Flavored Markdown extensions, and modern conventions for technical writing.