Syntax Highlighting
Nextra uses Shiki (opens in a new tab) to do syntax highlighting at build time. It's very reliable and performant. For example, adding this in your Markdown file:
```js
console.log('hello, world')
```
Results in:
console.log('hello, world')
Features
Inlined Code
Inlined syntax highlighting like let x = 1
is also supported via the
{:}
syntax:
Inlined syntax highlighting is also supported `let x = 1{:jsx}` via:
Highlighting Lines
You can highlight specific lines of code by adding a {}
attribute to the code
block:
```js {1,4-5}
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```
Result:
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
Highlighting Substrings
You can highlight specific substrings of code by adding a //
attribute to the
code block:
```js /useState/
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
You can highlight only a part of the occurrences of that substring by adding a
number it: /str/1
, or multiple: /str/1-3
, /str/1,3
.
Copy Button
By adding a copy
attribute, a copy button will be added to the code block when
the user hovers over it:
```js copy
console.log('hello, world')
```
Renders:
console.log('hello, world')
You can enable this feature globally by setting defaultShowCopyCode: true
in
your Nextra configuration (next.config.mjs
file). Once it's enabled
globally, you can disable it via the copy=false
attribute.
Line Numbers
You can add line numbers to your code blocks by adding a showLineNumbers
attribute:
```js showLineNumbers
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```
Renders:
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
Filenames and Titles
You can add a filename or a title to your code blocks by adding a filename
attribute:
```js filename="example.js"
console.log('hello, world')
```
Renders:
console.log('hello, world')
ANSI Highlighting
You can highlight ANSI escape codes:
```ansi
[0m [0;32m✓[0m [0;2msrc/[0mindex[0;2m.test.ts (1)[0m
[0;2m Test Files [0m [0;1;32m1 passed[0;98m (1)[0m
[0;2m Tests [0m [0;1;32m1 passed[0;98m (1)[0m
[0;2m Start at [0m 23:32:41
[0;2m Duration [0m 11ms
[42;1;39;0m PASS [0;32m Waiting for file changes...[0m
[0;2mpress [0;1mh[0;2m to show help, press [0;1mq[0;2m to quit
```
Renders:
✓ src/index.test.ts (1)
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 23:32:41
Duration 11ms
PASS Waiting for file changes...
press h to show help, press q to quit
Supported Languages
Check this list (opens in a new tab) for all supported languages.
With Dynamic Content
Since syntax highlighting is done at build time, you can’t use dynamic content in your code blocks. However, since MDX is very powerful there is a workaround via client JS. For example:
function hello () {
const x = 2 + 3
console.log(1)
}
This workaround has a limitation that updated content won't be re-highlighted.
For example if we update the number to 1 + 1
, it will be incorrectly
highlighted.
Check out the code (opens in a new tab) to see how it works.
Disable Syntax Highlighting
You can opt out of syntax highlighting for using one of your own. You can
disable syntax highlighting globally by setting codeHighlight: false
in your
Nextra configuration (next.config.mjs
file).
Option | Type | Description |
---|---|---|
codeHighlight | boolean | Enable or disable syntax highlighting. Defaults to `true`. |
Custom Grammar
Shiki accepts a VSCode TextMate Grammar (opens in a new tab) for syntax highlighting with custom language grammars.
You can provide these grammars by overriding the getHighlighter
function in
mdxOptions.rehypePrettyCodeOptions
option in your Nextra config inside
next.config.mjs
:
import { BUNDLED_LANGUAGES } from 'shiki'
nextra({
// ... other options
mdxOptions: {
rehypePrettyCodeOptions: {
getHighlighter: options =>
getHighlighter({
...options,
langs: [
...BUNDLED_LANGUAGES,
// custom grammar options, see the Shiki documentation for how to provide these options
{
id: 'my-lang',
scopeName: 'source.my-lang',
aliases: ['mylang'], // Along with id, aliases will be included in the allowed names you can use when writing markdown.
path: '../../public/syntax/grammar.tmLanguage.json'
}
]
})
}
}
})
Custom Themes
Within mdxOptions.rehypePrettyCodeOptions
you may also provide custom themes
instead of relying on CSS Variables:
nextra({
// ... other options
mdxOptions: {
rehypePrettyCodeOptions: {
// VSCode theme or built-in Shiki theme, see Shiki documentation for more information
theme: JSON.parse(
readFileSync('./public/syntax/arctis_light.json', 'utf8')
)
}
}
})
Multiple Themes
Nextra currently doesn't support specifying multiple themes. Because Shiki
renders multiple code blocks for every theme and tags them with an attribute
data-theme
, e.g. data-theme="dark"
.
However, in the future, multiple themes will be supported. You can track the progress in shikiji (newly fork of Shiki) https://github.com/antfu/shikiji#multiple-themes (opens in a new tab) that already supports multiple themes without rendering multiple code blocks.