Syntax Highlighting in Gatsby MDX

27 Jan 2020

1 min read

Photo by Estée Janssens

In this post, we will be looking into how to enable syntax highlighting for Gatsby MDX files using prism-react-renderer

first, we need to install a couple of modules

npm i prism-react-renderer @mdx-js/react

Then, we create the component which we will use in the <MDXProvider/> that is going to provide a wrapper replacement for our application.

const component = {
  pre: (props) => {
    const className = props.children.props.className || '';
    const matches = className.match(/language-(?<lang>.*)/);
    return (
      <Highlight
        {...defaultProps}
        code={props.children.props.children}
        language={
          matches && matches.groups && matches.groups.lang
            ? matches.groups.lang
            : ''
        }
      >
        {({ className, style, tokens, getLineProps, getTokenProps }) => (
          <pre className={className} style={style}>
            {tokens.map((line, i) => (
              <div {...getLineProps({ line, key: i })}>
                {line.map((token, key) => (
                  <span {...getTokenProps({ token, key })} />
                ))}
              </div>
            ))}
          </pre>
        )}
      </Highlight>
    );
  },
};

To render the code block we added in MDX file to show up on the page we will access props of the child component of <pre/> component as markdown code blocks get compiled into a <pre/> component wrapping the code component. We can dive a level deeper to get the code by using props.children.props.children, as you can see from the screenshot below.

code-block

To get the language attribute of the code block rather than hardcoding the value we look into MDX code block as it comes through as a class name that will look like language-className (for example language-javascript).

```javascript
const text = 'World'
console.log(`Hello ${text}`)
```

To get the language defined in the code component in the MDX file, we will drill down at props.children.props.className, if there is no class name then we might have not put a language on our code block in which case we will default to empty strings. Then we use a name capture group in regex with the name lang to get the language of the language-className. if there are matches

const matches = className.match(/language-(?<lang>.*)/);

To enable syntax highlighting in MDX files by adding the following snippet below in gatsby-browser.js

import React from 'react';
import { MDXProvider } from '@mdx-js/react';
import Highlight, { defaultProps } from 'prism-react-renderer';

/* eslint-disable */
const component = {
  pre: (props) => {
    const className = props.children.props.className || '';
    const matches = className.match(/language-(?<lang>.*)/);
    return (
      <Highlight
        {...defaultProps}
        code={props.children.props.children}
        language={
          matches && matches.groups && matches.groups.lang
            ? matches.groups.lang
            : ''
        }
      >
        {({ className, style, tokens, getLineProps, getTokenProps }) => (
          <pre className={className} style={style}>
            {tokens.map((line, i) => (
              <div {...getLineProps({ line, key: i })}>
                {line.map((token, key) => (
                  <span {...getTokenProps({ token, key })} />
                ))}
              </div>
            ))}
          </pre>
        )}
      </Highlight>
    );
  },
};

export const wrapRootElement = ({ element }) => {
  return <MDXProvider components={component}>{element}</MDXProvider>;
};

Following the post and you can find a starter repo here that shows usage of mdx pages and syntax highlighting

Subscribe to the newsletter

subscribe to get my latest content, favourite articles from the web and additional details about my launches, products, and experiments