Article thumbnail

Share

Add syntax highlighting in React using markdown-to-jsx and react-syntax-highlighter

Profile picture of author
Rowin van Amsterdam
-

In markdown, you can add code snippets by using the backtick character. This is a great way to add code snippets to your blog posts for example. However, it is not very easy to read. This is where syntax highlighting comes into play. You could do this easily by using the markdown-to-jsx and react-syntax-highlighter packages.

Check if code snippet is inline or block

The markdown-to-jsx package allows you to convert markdown to JSX. While I was using it, I found out that it does not support syntax highlighting out of the box. So I had to find a way to override a code block to add syntax highlighting.

Natively markdown-to-jsx uses a code tag for inline code and it will be wrapped in a pre tag if it's a code block. Something we can use to our advantage, because we only want to highlight code blocks.

To override default components we can add markDownToJsxOptions, like so:

import MarkdownToJsx from "markdown-to-jsx";
import { RichArticlePreBlock } from "./components/RichArticlePreBlock";

type MarkdownProps = {
    content: string;
};

export const Markdown = ({content}: MarkdownProps) => {
    const markDownToJsxOptions = {
        overrides: {
            pre: RichArticlePreBlock,
        }
    };

    return (
            <MarkdownToJsx options={markDownToJsxOptions}>
                {content}
            </MarkdownToJsx>
    );
};

In our RichArticlePreBlock component we have to check if children contains content that is of type code. If that's the case, we pass the content and the className to our RichArticleCodeBlock component, where we will highlight it using react-syntax-highlighter. Otherwise just return it as is. Because remember, we only want to highlight code that is wrapped inside a pre tag.

import { RichArticleCodeBlock } from "./RichArticleCodeBlock";

type RichArticlePreBlockProps = {
    children: JSX.Element | JSX.Element[];
};

export const RichArticlePreBlock = ({ children, ...rest }: RichArticlePreBlockProps) => {
    if ("type" in children && children["type"] === "code") {
        return RichArticleCodeBlock({ children: children["props"]["children"], className: children["props"]["className"] });
    }
    return <pre {...rest}>{children}</pre>;
};

Highlight the code block

Now we know what to highlight, we can use the react-syntax-highlighter package. Highlighting the code is pretty straightforward. You just have to import Prism (PrismLight if you want to use the light build) and pass the language and children to it. You can use HighlightJS instead of PrismJS if you want.

import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
import tsx from "react-syntax-highlighter/dist/cjs/languages/prism/tsx";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";

SyntaxHighlighter.registerLanguage("tsx", tsx);

type RichArticleCodeBlockProps = {
    children: string;
    className: string;
};

export const RichArticleCodeBlock = ({ children, className }: RichArticleCodeBlockProps) => {
    const language = className?.replace("lang-", "");

    return (
        <SyntaxHighlighter language={language} style={oneDark}>
            {children}
        </SyntaxHighlighter>
    );
};

You can also add a theme that you like if you want to. Themes for PrismJS can be found here and for HighlightJS here. If no theme is specified or the theme is not found, it will use the default one.

As you can see I used the light build of Prism. This is because I don't want to load the entire PrismJS library. I only want to load the languages I'll use, so that the bundle size remains small. Keep in mind by using the light build you have to register the languages you want by using the registerLanguage function:

SyntaxHighlighter.registerLanguage("tsx", tsx);

Supported languages for PrismJS can be found here and for HighLightJS here.

Note that I used react-syntax-highlighter package to highlight my code with PrismJS. You can also use PrismJS directly, but I found this package to be easier to use.