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:
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
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>
)
}
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
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;
}
Now in your gatsby-browser.js
file, import the file we just created.
// gatsby-browser.js
// custom CSS styles
import './src/style.css'
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`
// gatsby-config.js
module.exports = {
plugins: [
//...
{
resolve: `gatsby-plugin-mdx`,
options: {
gatsbyRemarkPlugins: [
- `gatsby-remark-prismjs`,
],
},
}
]
}
Code Block Styling Looks Awful
When the first time I added code highlighting, it looked like this:
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"
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)
meta with lineNumbers is not available in the props. Stuck on this.