Markdown Code Blocks and Syntax Highlighting

How to use inline code, fenced code blocks, and syntax highlighting in markdown. Language support and rendering differences.

· · 5 min read

Last updated: March 3, 2026

markdown code syntax-highlighting

Markdown has two code constructs: inline code for short snippets within a sentence, and code blocks for multi-line programs. Fenced code blocks (triple backticks with a language identifier) tell the renderer which syntax highlighter to apply. This gives you colored, readable code without any manual formatting.

Inline Code

Wrap text in single backticks to mark it as code within a paragraph.

Run `npm install` to install dependencies.
The `useState` hook returns a state variable and a setter.

Inline code renders in a monospace font with a subtle background. Use it for function names, CLI commands, file paths, variable names, and anything that’s “code” in a prose context.

Backticks inside inline code

If your code snippet contains a backtick, wrap it with double backticks:

`` `template literal` ``

If it contains double backticks, use triple:

``` `` nested `` ```

The outer backticks need a space between them and the content. Without the space, some parsers get confused.

Fenced Code Blocks

Three backticks on their own line start a code block. Three more end it.

```
function add(a, b) {
return a + b;
}
```

This renders as a plain monospace block with no syntax coloring. To get highlighting, add a language identifier right after the opening backticks.

```javascript
function add(a, b) {
return a + b;
}
```

The language identifier (javascript in this case) tells the renderer which grammar to use for tokenizing and coloring the code.

Language Identifiers

Every syntax highlighter supports a slightly different set of identifiers, but the common ones are universal. Here are the most-used identifiers and their aliases:

LanguageIdentifiers
JavaScriptjavascript, js
TypeScripttypescript, ts
Pythonpython, py
Bash / Shellbash, sh, shell, zsh
JSONjson
HTMLhtml
CSScss
SQLsql
Gogo
Rustrust, rs
Rubyruby, rb
Javajava
Cc
C++cpp, c++
C#csharp, cs
PHPphp
Swiftswift
Kotlinkotlin, kt
YAMLyaml, yml
TOMLtoml
Markdownmarkdown, md
Dockerfiledockerfile, docker
GraphQLgraphql, gql

Use the short alias when one exists. ts is easier to type than typescript, and every highlighter recognizes both.

Diff Highlighting

Use diff as the language identifier to highlight added and removed lines. Prefix lines with + for additions and - for removals.

```diff
- const API_URL = "http://localhost:3000";
+ const API_URL = process.env.API_URL;
function fetchData() {
- return fetch(API_URL + "/data");
+ return fetch(`${API_URL}/data`);
}
```

Lines starting with + render in green (or similar). Lines starting with - render in red. Lines with no prefix (or a space prefix) render as context. This is the same format Git uses for diffs, so you can paste git diff output directly into a fenced block.

Indented Code Blocks

The original markdown spec defines code blocks as lines indented by four spaces (or one tab):

Regular paragraph.
function legacy() {
return "four spaces of indentation";
}
Back to regular paragraph.

This syntax still works, but fenced code blocks are better in every way: they support language hints, they’re easier to see in source, and they handle edge cases around list nesting more predictably. Prefer fenced blocks.

Showing Markdown Inside Markdown

Sometimes you need to document markdown syntax itself. The trick is to use more backticks for the outer fence than the inner one.

If your example uses triple backticks, wrap it with quadruple backticks:

````markdown
```python
print("hello")
```
````

You can stack as many as you need. Five backticks around four, six around five, and so on. As long as the outer fence has more backticks than any inner fence, the parser handles it correctly.

Another approach: use tildes (~) for the outer fence:

~~~markdown
```python
print("hello")
```
~~~

Tildes and backticks are interchangeable for fenced code blocks. Mixing them is a clean way to nest code examples.

How Different Renderers Handle Code

Not all markdown renderers produce the same output for code blocks. Here’s how the major ones differ:

GitHub uses its own syntax highlighter based on Linguist. It supports hundreds of languages. Unrecognized identifiers fall back to plain text with no errors.

VS Code (preview panel) uses the same TextMate grammars as the editor itself. Highlighting in the preview matches the editor. Extensions can add more grammars.

Obsidian uses Prism.js by default. It covers all common languages. Community plugins can swap in different highlighters like Shiki.

MDtoLink uses highlight.js, which supports 190+ languages out of the box. The Markdown to HTML tool applies the same highlighting, so the output you preview is what your readers see.

Static site generators (Astro, Next.js, Hugo) each bundle their own highlighter. Astro uses Shiki by default. Next.js with MDX commonly uses rehype-pretty-code (also Shiki-based). Hugo has Chroma, a Go port of Pygments.

The takeaway: write standard fenced code blocks with recognized language identifiers, and your code will look good everywhere. Avoid renderer-specific extensions unless you’re locked into one platform.

Line Highlighting and Metadata

Some renderers support extra annotations after the language identifier:

```javascript title="server.js" {3-5}
import express from "express";
const app = express();
app.get("/", (req, res) => res.send("OK"));
app.listen(3000);
```

The title adds a filename label. The {3-5} highlights lines 3 through 5. This is not part of any markdown spec; it’s a convention used by Shiki, rehype-pretty-code, and some documentation frameworks.

If you’re writing for multiple platforms, skip these annotations. They render as plain text in renderers that don’t understand them.

Practical Tips

Pick a consistent style. Use fenced blocks everywhere. Use lowercase language identifiers. Use the short alias when available.

Don’t over-highlight. Small config snippets (a single JSON key, a one-line shell command) often read fine as inline code. Save fenced blocks for multi-line examples.

Test your output. Different highlighters tokenize differently. A Python block might look fine on GitHub but odd in your docs framework. Preview before publishing.

Keep examples runnable. If you show a code block, make sure a reader could copy-paste it and run it without modifications (or mark where they need to change values).

Write your markdown with code blocks and preview the highlighted output in real time using the Markdown to HTML tool. It supports all 190+ highlight.js languages and matches what your published documents look like.


David Schemm
David Schemm

Founder, MDtoLink

David builds developer tools and writes about markdown workflows, documentation, and AI-assisted publishing.

Publish your markdown to a shareable URL

One command. Free to start. No credit card.