Skip to content

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.

Wrap a word or phrase in single backticks:

mdx
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.

Wrap the block in triple backticks and put the language right after the opening fence:

mdx
```js
const 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 title if 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.

Options go on the opening fence, after the language, separated by spaces. Several can be combined:

mdx
```js title="server.js" {3} showLineNumbers=false
import 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

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:

src/server.js
```js
const 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.

Put line numbers and ranges in braces:

mdx
```js {2-3}
const app = express();
app.use(auth());
app.use(rateLimit());
app.listen(3000);
```

Here is the same block, rendered:

js
const app = express();
app.use(auth());
app.use(rateLimit());
app.listen(3000);

For a before-and-after on specific lines, mark them with ins and del. Both take the same range syntax as highlighting:

mdx
```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:

mdx
```diff lang="js"
- const timeout = 5000;
+ const timeout = 30000;
```

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.

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.

When you show the same operation in several languages, use CodeGroup. Each fenced child becomes one tab:

mdx
<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.

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 code prop, 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 language prop.

  • Loose text between the tags, such as <CodeBlock language="sh">echo "hi"</CodeBlock>. Straight quotes stay straight and an entity such as &amp; becomes &, but a bare { still opens a JavaScript expression and fails the page with unparseable-mdx. Use the code prop 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.