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>
  )
}
  1. We need to pull the language off of our codeblock and we will get that from the className prop. We will make sure to use a big regex magic to get the language that we want. And pass that as a value to the language prop on <Highlight>.
  2. We will pass in the children as a value to the code prop 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:

  1. Using other preset themes
  2. 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!