Skip to content
All posts
7 min read
markdown
code blocks
syntax highlighting
GFM

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:

  1. Inline code — for short snippets within a sentence
  2. Indented code blocks — for multi-line code in plain CommonMark
  3. 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:

LanguageHint
JavaScriptjs or javascript
TypeScriptts or typescript
Pythonpy or python
Shell / bashsh, bash, shell
HTMLhtml
CSS / SCSScss or scss
JSONjson
YAMLyaml or yml
SQLsql
Markdownmd or markdown
Rustrs or rust
Gogo
Javajava
C / C++c / cpp
Diffdiff
Mermaidmermaid (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.

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.