Migrating from gatsby-remark-prismjs to prism-react-renderer
I am gonna share a bit about how I have integrated with prism-react-renderer and kept the
styles I was using back with gatsby-remark-prismjs. As I migrate my blog over, I want to record
a bit better about the tools I have been using. One of the most important things for me
as I switch to MDX has been maintaining the readability of my code blocks. I won’t be talking
about how to install MDX, but the maintainers have a
great set of docs about it for Gatsby (which is what I use)!
Adding a CodeBlock component to MDX
In a layout file (typically the topmost one), import MDXProvider. This is where one can add in
custom components that will override how their markdown renders it. These each match to some
shortcode. In my case, I needed to declare the component
for code. I went to my src/pages/layout.js and added:
// src/pages/layout.js
import React, {Fragment} from 'react'
import {MDXProvider} from '@mdx-js/react'
import CodeBlock from 'src/components/CodeBlock'
// ... some more imports
const components = {
pre: props => <div {...props}>,
code: CodeBlock
}
const Layout = ({ children }) => (
<MDXProvider
components={components}
>
{/* */}
</MDXProvider>
)
The pre will always wrap around our code by default so we need to make sure
to pass through any props we receive down to the code block. In addition, we
can pass in any additional styles on the pre wrapper. I do this so I can
support smaller devices with my line highlighting.
Writing our CodeBlock component
We told our layout.js that we should be using our CodeBlock component so let’s go ahead and create that.
We’ll use prism-react-renderer’s Highlight component to handle how to present everything on the screen.
This was an almost exact copy from MDX’s Syntax Highlight documentation
but some important things worth noting are:
// src/components/CodeBlock.jsx
import React from 'react'
import Highlight, { defaultProps } from 'prism-react-renderer'
export default ({ children, className }) => {
// Pull the className
const language = className.replace(/language-/, '') || ""
return (
<Highlight {...defaultProps}
code={children}
language={language}
>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={className} style={{ ...style }}>
{tokens.map((line, index) => {
const lineProps = getLineProps({ line, key: index })
return (
<div key={index} {...lineProps}>
{line.map((token, key) => (
<span key={key}{...getTokenProps({ token, key })} />
))}
</div>
)
}
)}
</pre>
)}
</Highlight>
)
}
- We need to pull the language off of our codeblock and we will get that from the
classNameprop. We will make sure to use a big regex magic to get the language that we want. And pass that as a value to thelanguageprop on<Highlight>. - We will pass in the
childrenas a value to thecodeprop on<Highlight>. Then we’ll have all the syntax highlighting working by default!
Removing gatsby-remark-prismjs
And now… we can just remove the plugin entirely from our gatsby-config.json and everything should still
work!
plugins: [
'gatsby-plugin-theme-ui',
'gatsby-plugin-mdx',
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
- {
- resolve: `gatsby-remark-prismjs`,
- options: {
- classPrefix: 'language-',
- inlineCodeMarker: `>`,
- showLineNumbersGlobal: false,
- noInlineHighlight: false,
- },
- },
// ....
]
}
}
]
Updating Theme
At this point there is nothing else left but customizations! If you’re like me, you want to make your site a bit personal so I will show you:
- Using other preset themes
- Reusing themes you had with
gatsby-remark-prismjs
Preset Themes
prism-react-renderer has a bunch of preset themes. You can find the list
here. By default, it uses the duotoneDark theme.
Import the specific theme you’d like and pass it as the value for the theme prop on Highlight:
// src/components/CodeBlock.jsx
import React from 'react'
import Highlight, { defaultProps } from 'prism-react-renderer'
import vsDark from 'prism-react-renderer/themes/vsDark';
export default ({ children, className }) => {
// Pull the className
const language = className.replace(/language-/, '') || ""
return (
<Highlight {...defaultProps}
code={children}
language={language}
theme={vsDark}
>
{/* ... */}
</Highlight>
)
}
Reusing the gatsby-remark-prismjs theme
You might have had a custom theme installed through your gatsby-browser. I have been using
prism-atom-dark theme for a long time and it has been great! To keep using it, I just needed
to tell the theme is undefined. This is an escape hatch that let’s prism-react-renderer know
we will handle the themes with global CSS declared elsewhere.
Make sure in you have your css file imported:
// gatsby-browser.js
// This is the source path to my CSS theme
require('./src/assets/css/prism-atom-dark.css')
Then in our CodeBlock component:
// src/components/CodeBlock.jsx
import React from 'react'
import Highlight, { defaultProps } from 'prism-react-renderer'
export default ({ children, className }) => {
// Pull the className
const language = className.replace(/language-/, '') || ""
return (
<Highlight {...defaultProps}
code={children}
language={language}
theme={undefined}
>
{/* ... */}
</Highlight>
)
}
Conclusion
And that’s a wrap! This was an excellent fun project for me over the weekend. There are a few things I will
expand on, like how do I keep my line highlighting now that I am using prism-react-renderer! Let me know if this
helped you out!
P.S. Thank you to Ryan Warner for inspiring me to take on this project!