DEV Community

Aldo Portillo
Aldo Portillo

Posted on

Syntax Highlighting in a React Blog

Introduction

I am restructuring the blog page for my personal portfolio. At first, I was using a server I created using MongoDB, Express.js and Node. Although I was a fan of creating my own server to host my blogs. I found myself not writing that many blogs. This is when I discovered DEV.to. If you are reading this on DEV.to then you already know what that is. If you are reading this on my site, Dev.to is a place where developers can write blogs. Since Dev.to is for developers. I figured it had to have an API. It did. I decided I wanted to use Dev.to to hold my blogs for my portfolio.

Approach

I simply called the API and saw that I could access the HTML and Markdown for every blog post. I thought this was going to be simple and only take me 30 minutes to completely integrate. My first issue came when I thought that I could just return the HTML I got from the API.


export default function Blog() {
    const [content, setContent] = React.useState(-set content after API call-)
    //Some Code

    return(
        {content.body_html}
    )
}
Enter fullscreen mode Exit fullscreen mode

This simply returned all text including the tags. Well simple I could just useEffect to change innerHTML. Thats perfect except for the fact that using document.innerHTML can leave you open to XSS vulnerabilities.

Well how can I do this? Maybe I can render the markdown? Quick google search lead me to React Markdown. React Markdown allows you to render markdown in your react file.


import ReactMarkdown from 'react-markdown'

export default function Blog() {
    const [content, setContent] = React.useState(-set content after API call-)
    //Some Code

    return(
        <ReactMarkdown children={content.body_markdown}>
    )
}
Enter fullscreen mode Exit fullscreen mode

Everything looked great, but I realized my code blocks were not styled.

Styling Code Blocks

Although React Markdown renders your markdown. There isn't much styling other than default styling. I found two syntax highlighters that people recommended: Highlight.js and Prism. When I used highlight. I simply imported the JS and Stylesheet in my html head.

This worked at first. But due to the blogs only being rendered at the moment the page is navigated to, everything in the head tag had already been applied.

Next approach, switch to Prism and use the React hook useEffect to load the styles when a new blog is open. Unfortunately, this returned a XSS venerability due to Prism taking the pre and code blocks and adding styling to it after it was rendered. I'm guessing document.innerHTML was used. I read about the error and some people said it was fine while others offered a solution by using escape characters. Using escape characters lead me back to my first issue on the way html was being rendered.

The Meat and Potatoes

Since setting innerHTML is frowned upon for security reasons, I figured I couldn't style the code blocks after the render. And due to the blogs not being loaded until they are navigated to, I definitely couldn't do style them before. That left me with the option of styling the codeblocks as the markdown is being rendered.

After reading the React Markdown documentation the answer was right in front of my eyes the entire time. Props. React markdown accepts a children prop indicating what will be getting rendered but it also accept a components prop. The component prop is in charge of mapping tag names to React components. This means that we can set the classes and tags as we render the markdown.

export default function Blog() {

    //Some Code

  return (
    <div className='blog'>
      <h1>{content.title}</h1>

      <ReactMarkdown
        children={content.body_markdown}
        components={{
          code({node, inline, className, children, ...props}) {
            const match = /language-(\w+)/.exec(className || '')
            return !inline && match ? (
              <SyntaxHighlighter
                {...props}
                children={String(children).replace(/\n$/, '')}
                style={oneDark}
                language={match[1]}
                PreTag="div"
              />
            ) : (
              <code {...props} className={className}>
                {children}
              </code>
            )
          }
        }} />
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Here we are taking the code block and giving it a class name 'x language' in which x is the name of the language given after the three back ticks. If the class name is equal to a class name that Syntax Highlighter supports. We render the code block using SyntaxHighlighter otherwise we render the code block was normal.

Top comments (0)