Guides / Syntax highlighting
This guide explores how to apply syntax highlighting to code blocks. MDX supports standard markdown syntax (CommonMark). It does not apply syntax highlighting to code blocks by default.
There are two ways to accomplish syntax highlighting: at compile time or at runtime. Doing it at compile time means the effort is spent upfront so that readers will have a fast experience as no extra code is sent to them (syntax highlighting needs a lot of code to work). Doing it at runtime gives more flexibility by moving the work to the client. This can result in a slow experience for readers though. It also depends on what framework you use (as in it’s specific to React, Preact, Vue, etc.)
Use either rehype-highlight
(highlight.js
) or @mapbox/rehype-prism
(Prism) by doing something like this:
import {compile} from '@mdx-js/mdx'
import rehypeHighlight from 'rehype-highlight'
const code = `~~~js
console.log(1)
~~~`
console.log(
String(await compile(code, {rehypePlugins: [rehypeHighlight]}))
)
<>
<pre>
<code className="hljs language-js">
<span className="hljs-variable language_">console</span>.
<span className="hljs-title function_">log</span>(
<span className="hljs-number">1</span>)
</code>
</pre>
</>
Important: If you chose rehype-highlight
, then you should also use a highlight.js theme somewhere on the page. For example, to get GitHub Dark from cdnjs:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css">
If you chose @mapbox/rehype-prism
, include something like this instead to get Prism Dark:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/themes/prism-dark.min.css">
Use for example react-syntax-highlighter
, by doing something like this:
import SyntaxHighlighter from 'react-syntax-highlighter'
import Post from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
<Post components={{code}} />
function code({className, ...properties}) {
const match = /language-(\w+)/.exec(className || '')
return match
? <SyntaxHighlighter language={match[1]} PreTag="div" {...properties} />
: <code className={className} {...properties} />
}
<>
<pre>
<div
className="language-js"
style={{
background: '#F0F0F0',
color: '#444',
display: 'block',
overflowX: 'auto',
padding: '0.5em'
}}
>
<code style={{whiteSpace: 'pre'}}>
<span>console.</span>
<span style={{color: '#397300'}}>log</span>
<span>(</span>
<span style={{color: '#880000'}}>1</span>
<span>)</span>
</code>
</div>
</pre>
</>
meta
fieldMarkdown supports a meta string for code:
```js filename="index.js"
console.log(1)
```
The meta
part is everything after the language (in this case, js
). This is a hidden part of markdown: it’s normally ignored. But as the above example shows, it’s a useful place to put some extra fields.
@mdx-js/mdx
doesn’t know whether you’re handling code as a component or what the format of that meta string is, so it defaults to how markdown handles it: meta
is ignored.
But what if you want to access meta
at runtime? That’s exactly what the rehype plugin rehype-mdx-code-props
does. It lets you type JSX attributes in the meta
part which you can access by with a component for pre
.
That plugin, like all rehype plugins, can be passed as rehypePlugins
in ProcessorOptions
. More info on plugins is available in § Extending MDX