DEV Community

Sasmitha Manathunga
Sasmitha Manathunga

Posted on

Adding Syntax Highlighting with Line Numbers to Gatsby MDX Using prism-react-renderer

In this article, we will go through the steps on how to enable code highlighting with line numbers for Gatsby MDX files with prism-react-renderer.

If you already haven’t integrated MDX into your project (you should because MDX is awesome), here’s the official guide on Gatsby's documentation to add it to your project. However, if you are already using Markdown Remark in your project, consider Migrating to MDX. In this post, we will integrate PrismJS syntax highlighting with MDX using prism-react-renderer. Also, we are going to add line numbers to code blocks. This is what we are aiming for:

Highlighted Code Block


Step 1: Install prism-react-renderer Package

To get started, you need to install prism-react-renderer, which wraps PrismJS into a React component.

npm install prism-react-renderer
Enter fullscreen mode Exit fullscreen mode

When you add a fenced code block in your .mdx file, the gatsby-plugin-mdx will wrap your code in a <pre> element and append a class name to it indicating the programming language. prism-react-renderer uses this to identify the language and highlight the code block.


Step 2: Create the CodeBlock Component

Let's create a React component to add code highlighting with line numbers(optional) to our code blocks. First, create a file CodeBlock.jsx in src/components/, then add the following contents.

// CodeBlock.jsx
import React from 'react'
import Highlight, { defaultProps } from 'prism-react-renderer'
import theme from 'prism-react-renderer/themes/vsDark'

export default function CodeBlock(props) {
  const className = props.children.props.className || ''
  const matches = className.match(/language-(?<lang>.*)/)
  const showLineNumbers = props.children.props.lineNumbers
  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 })}>
              {showLineNumbers && <span className='line-number'>{i + 1}</span>}
              {line.map((token, key) => (
                <span key={key} {...getTokenProps({ token, key })} />
              ))}
            </div>
          ))}
        </pre>
      )}
    </Highlight>
  )
}
Enter fullscreen mode Exit fullscreen mode

This component highlights code blocks. Optionally, you can add line numbers by passing the meta string lineNumbers to the code block in the .mdx file. If you want a different theme, change the theme import to an available theme of your liking.


Step 3: Add the CodeBlock Component to <MDXProvider>

Go to the file where you use the <MDXRenderer> component (in my case src/templates/blog-post.js which renders my blog posts) and wrap it with the <MDXProvider> component. Then add the CodeBlock component to the components object and pass it to the <MDXProvider> component.

// blog-post.js
// ...
import { MDXProvider } from '@mdx-js/react'
import CodeBlock from '/src/components/CodeBlock.jsx'

const components = {
  pre: CodeBlock,
}

const BlogPostTemplate = ({ data, location }) => {
  const post = data.mdx
  //...

  return (
    //...
    <MDXProvider components={components}>
      <MDXRenderer>{post.body}</MDXRenderer>
    </MDXProvider>
    //...
  )
}

export default BlogPostTemplate
Enter fullscreen mode Exit fullscreen mode

Step 4: Styling Line Numbers

To render the line numbers correctly, we will add some styling. Create a file style.css in your src folder and add the following CSS styles. If you already have a global CSS file, append the following styles to it.

/* style.css */

.line-number {
  text-align: right;
  padding-right: 1em;
  user-select: none;
  opacity: 0.5;
}
Enter fullscreen mode Exit fullscreen mode

Now in your gatsby-browser.js file, import the file we just created.

// gatsby-browser.js

// custom CSS styles
import './src/style.css'
Enter fullscreen mode Exit fullscreen mode

Step 5: Add Code to .mdx Files

To test code highlighting, open up one of your .mdx files and add some code. If you want line numbering, just pass the meta string lineNumbers after the language declaration. Note: The space is important.


```javascript lineNumbers
function add(num1, num2) {
  const result = num1 + num2
  return result
}

console.log(add(1, 2))
```

And that's it! Save your files, run gatsby develop, and go to the pages created from .mdx files to see your code blocks highlighted and optionally numbered. If something went wrong, let's put on the debugging glasses and get to work.


Debugging Errors

Here're some errors that popped up when I was integrating prism-react-renderer with MDX.

props.children.props.children.trim is not a function

My blog previously used gatsby-remark-prismjs to highlight code blocks, which conflicted with prism-react-renderer. So I uninstalled it and removed it from the plugins array in gatsby-config.js.

npm remove gatsby-remark-prismjs`
Enter fullscreen mode Exit fullscreen mode
// gatsby-config.js
module.exports = {
  plugins: [
    //...
    {
      resolve: `gatsby-plugin-mdx`,
      options: {
        gatsbyRemarkPlugins: [
-         `gatsby-remark-prismjs`,
        ],
      },
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Code Block Styling Looks Awful

When the first time I added code highlighting, it looked like this:
Broken Code Highlighting Image

Ah, Awful! What's going on? This is the same problem as above, I forgot to remove the existing CSS styles used by gatsby-remark-prismjs in gatsby-browser.js. This caused conflicting styles and resulted in the above mess. To fix this, simply remove the CSS import in gatsby-browser.js.

// gatsby-browser.js
- import "prismjs/themes/prism.css"
Enter fullscreen mode Exit fullscreen mode

Conclusion

If you fixed the bugs and everything went right, congrats🎉. Otherwise, check out the official documentation for gatsby-plugin-mdx, MDX, and prism-react-renderer. Happy coding!

Top comments (1)

Collapse
 
subhashkarupusamy profile image
Subhash

meta with lineNumbers is not available in the props. Stuck on this.