DEV Community

Miguel Crespo
Miguel Crespo

Posted on • Originally published at miguelcrespo.co

How to render pretty code blocks using Contentful Rich Text Editor and Gatsby

In a previous post we learned how to render YouTube videos using the Rich Text Editor from Contentful, in this post we will learn how to render pretty code blocks using the Rich Text Editor from Contentful, I will assume that you already know:

With that said, let's start!

Adding code snippets to your post content

We will use the built-in code option to insert our snippets to the posts, the problem I faced was that Contentful doesn't allow to specify the language of a snippet like the Github Flavored Markdown, so we cannot do things like this to manage highlighting:



``javascript
console.log("Hello World")
``


Enter fullscreen mode Exit fullscreen mode

Instead, what I came up with was to use the first line of the snippet for metadata, like the language of the snippet, in that way the snippet above would be written like:



---
lang:javascript
console.log("Hello World")


Enter fullscreen mode Exit fullscreen mode

Later in the code we will handle this metadata information, for now let's add it to the content of our post

Adding code highlighting to our blog

I was checking, and the most popular library by far seem to be PrismJS, it's easy to add to our Gatsby website, and it's easy to configure

Installation

To install it, you just need to run this

npm install prismjs babel-preset-gatsby babel-plugin-prismjs

Configuring PrismJS

We installed the babel-babel-preset-gatsby package to be able to tell prismjs which theme to use, and exactly which languages and plugins we want to support in our side and therefore not include unused features, for this we need to create a new file called:
.babelrc



{
  "presets": [
    "babel-preset-gatsby"
  ],
  "plugins": [
    [
      "prismjs",
      {
        "languages": [
          "javascript",
          "css",
          "markup"
        ],
        "plugins": [
          "show-language",
          "line-numbers"
        ],
        "theme": "tomorrow",
        "css": true
      }
    ]
  ]
}


Enter fullscreen mode Exit fullscreen mode
  • You can see in the snippet above that the languages we will support in the blog are: javascript, css, markup

    • To see a list of all the supported languages, check here
  • The plugins we support are show-language and line-numbers

Finally, enabling PrismJS in our application

The last step is simply to call Prismjs to start coloring all the <code> tags that it finds, I did it by calling the function inside a useEffect in the React Component that is rendering the blog post



useEffect(() => {
  // call the highlightAll() function to style our code blocks
  Prism.highlightAll();
}, []);


Enter fullscreen mode Exit fullscreen mode

Rendering the right HTML tags

In the section of the code where we render the basic content of the Rich Text Editor, we should add a new option to the renderRichText function, this new option is just a property inside the key renderMark (A code block is a mark in the Contentful language), so it would look like this



renderRichText(post.body, {
  renderMark: {
    [MARKS.CODE]: (text) => {
      return (
        <pre>
          <code>{text}</code>
        </pre>
      );
    },
  },
});


Enter fullscreen mode Exit fullscreen mode

In the code above, every time we find a code block inside the Contentful rich text editor content, we will render the text inside a <pre><code> tags, this is how Prismjs is expecting our code to be

Extracting the language from the code snippet

Now we want to extract the language from the first line of the code snippet, we will look o line to avoid weird behaviors later, and we will use the following regular expression ^lang:(\w+) to test the code snippet.

  • If the expression doesn't match, we'll render a basic code block

  • Otherwise, we will render the fancy Prismjs elements

Final code



renderRichText(post.body, {
renderMark: {
[MARKS.CODE]: (text) => {
const regex = /^lang:(\w+)/;
// If the code snippet doesn't have the expected metadata
if (!regex.test(text)) {
return <code>{text}</code>;
}
// Extract the language
const language = regex.exec(text)[1];

// Remove the first line to avoid including metadata in the rendered version
return (
<pre>
<code className={language-</span><span class="p">${</span><span class="nx">language</span><span class="p">}</span><span class="s2"> line-numbers}>
{text.split("\n").slice(1).join("\n")}
</code>
</pre>
);
},
},
});

Enter fullscreen mode Exit fullscreen mode




Image

Final Code

Top comments (0)