Adding Syntax Highlighting to Gatsby MDX with Prism

Adding Syntax Highlighting to Gatsby MDX with Prism

James Charlesworth

By James Charlesworth

1 May 20203 min read

Gatsby is great and the plugin system is very widely developed, however sometimes it can be a bit tricky to get different libraries to work together. In this post we are going to look at the steps to integrate PrismJS syntax highlighting with MDX pages generated with the gatsby-plugin-mdx plugin. This is what we are going to enable:

```javascript
function myFunction() {
const iterable = [10, 20, 30];
for (const value of iterable) {
console.log(value);
}
}
``

And this will be the result

function myFunction() {
const iterable = [10, 20, 30];
for (const value of iterable) {
console.log(value);
}
}

Step 1: Add some code to your MDX

Before we add syntax highlighting let's add the javascript snippet to our MDX markup, run a gatsby build and inspect the generated HTML in your browser. You should see something like this:

Inspected Code

The gatsby-plugin-mdx plugin will wrap your code in a <code\> element, and then place this inside a <pre\> (for preformatted text). Also notice that gatsby-mdx will append the class name <code class="language-javascript">. This is all PrismJS needs to get to work to identify your code as Javascript (or any of the other languages that are supported out the box)

Step 2: Create a Highlight Wrapper

In order to add formatting to the <code></code> block above, we can use prism-react-renderer which nicely wraps PrismJS into a React element.

npm install prism-react-renderer

Following the docs for prism-react-renderer we can create a component in our Gatsby site to wrap the code block.

Add a new file ./src/components/CodeBlock.jsx with the following contents:

import React from 'react'
import Highlight, {defaultProps} from 'prism-react-renderer'
import theme from 'prism-react-renderer/themes/github'
import Prism from "prism-react-renderer/prism";
export default (props) => {
const className = props.children.props.className || ''
const matches = className.match(/language-(?<lang>.*)/)
return (
<Highlight {...defaultProps} code={props.children.props.children.trim()} language={
matches && matches.groups && matches.groups.lang
? matches.groups.lang
: ''
}
theme={theme}>
{({className, style, tokens, getLineProps, getTokenProps}) => (
<pre className={className} style={{...style, padding: '20px'}}>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({line, key: i})}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({token, key})} />
))}
</div>
))}
</pre>
)}
</Highlight>
)
}

This is a react component that wraps our content (from props.children) with the <Highlight /> element imported from prism-react-renderer.

Step 3: Supply Wrapper Component To MDXRenderer

The final step is to tell gatsby-plugin-mdx to use our new wrapper component whenever it wants to render a <pre> element.

If you are not already, wrap your MDX in a <MDXRenderer /> component as described in the docs for gatsby-plugin-mdx/#mdxrenderer. Then we can add our new CodeBlock wrapper into the components object supplied to the <MDXProvider />

import { MDXProvider } from "@mdx-js/react"
import CodeBlock from './path/to/CodeBlock'; // The wrapper you created in step 2
const components = {
pre: CodeBlock
}
const MdxBlock: React.FunctionComponent<Props> = (props) => {
return <MDXProvider components={components}>
<MDXRenderer>{props.mdx.body}</MDXRenderer>
</MDXProvider>
}
export default MdxBlock;

And there you have it. Build and run your gatsby site with gatsby develop and open the page created from your .mdx file to see all the glories of code highlighting!