The identifier straight after the opening backticks. It selects the Prism grammar and fills the language name on the right of the header bar. A fence with no identifier falls back to plain text.
Code Blocks
Every fenced code block in an MDX page renders through the same component: Prism.js syntax highlighting, a header bar carrying the file title and the language name, line numbers down the left edge, and a copy button. All of it is tuned from the metastring, the space-separated text that follows the language on the opening fence line. This page covers every option that metastring accepts, how language identifiers resolve, and what the copy button hands over.
Quick Start
A fence with nothing but a language identifier already gets highlighting, line numbers, a header bar, and a copy button.
async function fetchDocumentation(slug) {
const response = await fetch(`/api/docs/${slug}`)
return response.json()
}```javascript
async function fetchDocumentation(slug) {
const response = await fetch(`/api/docs/${slug}`)
return response.json()
}
```Adding Options
Options are space-separated and order never matters. This fence names a file and highlights the three lines that do the work.
import { SignJWT } from 'jose'
const secret = new TextEncoder().encode(process.env.JWT_SECRET)
export async function createSession(payload) {
return new SignJWT(payload).setProtectedHeader({ alg: 'HS256' }).sign(secret)
}```typescript {3-5} title="lib/auth.ts"
import { SignJWT } from 'jose'
const secret = new TextEncoder().encode(process.env.JWT_SECRET)
export async function createSession(payload) {
return new SignJWT(payload).setProtectedHeader({ alg: 'HS256' }).sign(secret)
}
```Advanced
All eight options stack on one fence. The block below sets a title, highlights
two ranges, starts numbering at 120, marks every SearchItem, and collapses to
six visible lines with a "Show more" button.
```typescript {2,5-7} title="lib/search-index.ts" showLineNumbers=120 /SearchItem/ expandable maxLines=6
export function buildSearchIndex(version?: string): SearchItem[] {
const searchIndex: SearchItem[] = []
const pages = collectPages(version)
for (const page of pages) {
const record: SearchItem = toSearchItem(page)
searchIndex.push(record)
}
return searchIndex
}
```Options
Each option below is matched independently against the metastring, so they can appear in any order. Only the first occurrence of an option counts. Text the parser doesn't recognize is ignored rather than treated as an error.
languagestringDefault: texttitle="filename"stringDefault: language nameFile path shown on the left of the header bar, in double or single quotes. Covered on File title.
{lines}number[]Lines to tint, as single numbers, ranges, or both: {1,3-5}. Covered on Line
highlighting.
showLineNumbersbooleanDefault: trueLine numbers are already on for every block, so writing this only makes the intent explicit. Covered on Line numbers.
showLineNumbers=NnumberDefault: 1Starts the gutter at line N instead of 1. Covered on Line numbers.
/pattern/stringMarks every literal occurrence of the text between the slashes. Covered on Word highlighting.
expandablebooleanDefault: falseCollapses the block behind a "Show more" button. Covered on Collapsible code.
maxLines=NnumberHow many lines stay visible while the block is collapsed. Covered on Collapsible code.
Diff markers are the one feature that lives in the code rather than the metastring: see Diff highlighting.
Languages
The language registry holds 71 identifiers and 49 aliases, so ts, yml, sh,
py, rs, tf, and dockerfile all resolve to their full names. The resolved
name is what appears on the right of the header bar: ts shows as TypeScript,
tf as HCL, wat as WebAssembly.
Two cases behave in ways the registry doesn't spell out. tsx borrows the
TypeScript grammar, so JSX inside a .tsx sample is highlighted as TypeScript.
An identifier with no loaded grammar degrades quietly instead of breaking the
page: the code renders escaped and unhighlighted, with the badge and the copy
button intact.
Terminal languages get their own header icon. bash, sh, shell, zsh,
powershell, terminal, and console show a terminal glyph; everything else
shows a file glyph.
npm install
npm run build
npm start```bash title="deploy.sh" {1,3}
npm install
npm run build
npm start
```Copy behavior
The copy button hands over the block's original source text, not the rendered
markup. Because CodeBlock draws line numbers with CSS counters, they never
reach the clipboard. Diff markers do; they are part of the source. And a
collapsed block copies whole, hidden lines included, since collapsing clips only
the height.