Format code
Fenced code blocks, the fence options Reed honours, and when to use CodeBlock and CodeGroup.
Reed renders fenced code blocks with Expressive Code, the engine Starlight uses. You get syntax highlighting, a copy button, line numbers and a header label on every block with no configuration. This page lists the fence options that work, and the ones from other platforms that are silently ignored.
Inline code
Section titled “Inline code”Wrap a word or phrase in single backticks:
Set `REED_TOKEN` before you run `reed build`.If the code itself contains a backtick, wrap it in double backticks with a space inside
each end: `` `code` ``.
Use inline code for anything a reader might type, paste or search for: commands, file names, environment variables, option names and literal values.
Fenced code blocks
Section titled “Fenced code blocks”Wrap the block in triple backticks and put the language right after the opening fence:
```jsconst client = new Client({ apiKey: process.env.API_KEY });```Always tag the language. The tag drives syntax highlighting, and Reed shows it as the
block’s header label when the block has no title. An untagged fence renders as plain text
with no highlighting and an empty header. For output, logs or anything that is not code,
tag it text.
Every block gets:
- A copy button. It copies the code without line numbers. In terminal blocks, comment lines are left out of the copied text.
- Line numbers. They are on by default.
- A header. It shows the
titleif you set one, and the language otherwise.
Blocks tagged with a shell language, such as sh, bash or console, render in a
terminal-style frame automatically. For commands a reader will paste, use sh or bash
and leave out the $ prompt: the copy button copies a console block’s $ along with
the command.
Fence options
Section titled “Fence options”Options go on the opening fence, after the language, separated by spaces. Several can be combined:
```js title="server.js" {3} showLineNumbers=falseimport express from 'express';const app = express();app.listen(3000);```Each option below was checked against a real Reed build.
| Option | Example | Effect |
|---|---|---|
| Title | title="server.js" |
Shows the value in the header, replacing the language label |
| Line highlight | {1,3-4} |
Highlights lines 1, 3 and 4 |
| Inserted lines | ins={2} |
Marks line 2 as added |
| Deleted lines | del={3} |
Marks line 3 as removed |
| Text highlight | "apiKey" |
Highlights every occurrence of the quoted text |
| Hide line numbers | showLineNumbers=false |
Removes the line-number gutter |
| Start line | startLineNumber=10 |
Numbers the first line 10 |
| Wrap | wrap |
Wraps long lines instead of scrolling sideways |
| Terminal frame | frame="terminal" |
Forces the terminal-style frame |
| No frame | frame="none" |
Removes the header, including the language label |
File-name comments
Section titled “File-name comments”A comment on the first line that looks like a file path becomes the block’s title, and the comment is removed from the code:
```jsconst app = express();```This renders with src/server.js in the header and one line of code. Use title when
you want the label without relying on the comment’s shape.
Line highlighting
Section titled “Line highlighting”Put line numbers and ranges in braces:
```js {2-3}const app = express();app.use(auth());app.use(rateLimit());app.listen(3000);```Here is the same block, rendered:
const app = express();app.use(auth());app.use(rateLimit());app.listen(3000);Show a change
Section titled “Show a change”For a before-and-after on specific lines, mark them with ins and del. Both take the
same range syntax as highlighting:
```js ins={2} del={3}const app = express();app.use(auth({ strict: true }));app.use(auth());```To show a whole patch, use the diff language. Lines starting with + and - are
marked. Add lang to keep syntax highlighting for the underlying language:
```diff lang="js"- const timeout = 5000;+ const timeout = 30000;```Options that do not work
Section titled “Options that do not work”These are accepted without an error and have no effect. They come from other platforms, mostly Mintlify, and Reed does not implement them:
| Option | Where it comes from |
|---|---|
highlight={1} |
Mintlify. Use {1} instead. |
focus={2} |
Mintlify. There is no fence equivalent. CodeBlock has a focus prop. |
lines |
Mintlify. Line numbers are already on. |
icon="..." |
Mintlify |
expandable |
Mintlify |
collapse={1-3} |
An Expressive Code plugin Reed does not load |
// [!code highlight], // [!code ++] |
Shiki’s comment notation. The comment is rendered as written. |
showLineNumbers on its own is harmless but does nothing, because line numbers are
already on.
Fences inside components
Section titled “Fences inside components”MDX parses a fenced block inside a JSX element differently from one at the top level, and it is a common source of confusing build failures. Keep fences at the top level of the page when you can. Put the prose in the component and the code directly after it. When a fence does belong inside a component, give the tags and the fence their own lines. The exact forms that fail, and the fix for each, are in Fix “Expected a closing tag” for a code block inside a component. See also MDX pitfalls.
Fences inside list items are fine. Indent the fence to line up with the item’s text.
Several languages
Section titled “Several languages”When you show the same operation in several languages, use CodeGroup. Each fenced child
becomes one tab:
<CodeGroup>
```js title="JavaScript"await client.projects.list();```
```python title="Python"client.projects.list()```
</CodeGroup>Give the tags and each fence their own lines, with blank lines between them.
Each tab is labelled from its own child: the fence’s title first, then its language tag
exactly as written, then Tab 1, Tab 2 and so on. Title each fence when the language tag
is not the name you want readers to see, such as js for JavaScript. A group can mix
fences and CodeBlock elements. The full rules are in
Steps, tabs and code.
Two fences in a row in different languages, with no CodeGroup, raise DS-D03, a warning
suggesting CodeGroup. It does not fail a build unless you pass --strict-components.
If you want the blocks stacked rather than tabbed, give each a title naming its language
and put a short sentence between them, such as “In Python:”. The sentence ends the run of
adjacent fences, so DS-D03 does not fire.
The CodeBlock component
Section titled “The CodeBlock component”CodeBlock is the JSX form of a single block. It renders through Expressive Code like a
fence, with the same highlighting, header and copy button, and shows the code exactly as
written. Its props, including highlight, focus, maxLines and wordWrap, are listed
in Steps, tabs and code.
It takes the code in one of three forms, in this order:
-
The
codeprop, as a string or template literal. Use it for code with quotes or braces:mdx <CodeBlock language="ts" focus="1" code={`const x = { mode: "strict" };`} /> -
A single fenced child, with the tags and the fence on their own lines. The fence’s language tag is used when there is no
languageprop. -
Loose text between the tags, such as
<CodeBlock language="sh">echo "hi"</CodeBlock>. Straight quotes stay straight and an entity such as&becomes&, but a bare{still opens a JavaScript expression and fails the page withunparseable-mdx. Use thecodeprop or a fence for code with braces.
A plain fence is still the simplest choice. Fences cover title, line highlighting, line
numbers, start line and wrapping. Reach for CodeBlock when you need focus or
maxLines.