Add highlight.js Syntax Highlighting to a Gatsby-rendered Drupal Website

When every tutorial for syntax highlighting in Gatsby talks about highlight.js markdown but you have a Drupal website, you need to write your own for posterity. Here's mine.

This website uses the very helpful CKEditor CodeSnippet module to provide syntax highlighting. That in turn uses highlight.js. However, while investigating using Gatsby for the frontend of this website, I came across the headache of adding code highlighting to the site, when my code is just a string in the body field. Here's my solution ... if you have a better one, let me know in the comments.

Create custom utility function for highlight.js

  • Add highlight.js to your repo npm install highlight.js
  • Create a file at /src/utils/highlightCode.js.
  • Import the library and the CSS style you want to use
  • Set the languages you want to load (we don't need to load all of them for performance reasons)
  • Create a function in this file that finds every instance of your code element and loop through it to let highlight.js work on each instance. That file will look something like this:
import hljs from 'highlight.js'
import 'highlight.js/styles/monokai-sublime.css'

hljs.configure({
  languages: [
    'php',
    'javascript',
    'json',
    'bash',
    'scss',
    'css',
    'yml',
    'twig',
    'html',
  ],
})

export default function highlightCode() {
  const codeBlocks = document.querySelectorAll('pre > code')
  codeBlocks.forEach(codeBlock => {
    if (typeof codeBlock === 'object') {
      hljs.highlightBlock(codeBlock)
    }
  })
}

Use the useEffect() hook to apply this to our template

In my case, I wanted to apply this to the article template, which means attaching it to the

<div dangerouslySetInnerHTML={{ __html: nodeArticle.body.value }} />

but by the time Gatsby has grabbed and rendered that, it's too late. Instead, we add our useEffect() before we render our article - this is basically the hook_preprocess_node() of React! That's looks like this:

// import React, etc, etc

import highlightCode from '../../utils/highlightCode'

export default function SingleArticlePageTemplate({ data }) {
  useEffect(() => {
    highlightCode()
  })
  return (
    <>
      <Layout>

        // Stuff before body field (title, image, etc)

            <div dangerouslySetInnerHTML={{ __html: articleBody }} />

        // Stuff after body field (tags, etc)

      </Layout>
    </>
  )
}

It's pretty simple, hopefully it will help you/save you some time Googling.

Filed Under:

  1. Drupal Planet
  2. Drupal
  3. Decoupled Websites
  4. Gatsby