React Componenets in Markdown articles

Pavel Polívka - Dec 15 '21 - - Dev Community

As I decided to create my blog I choose Next.js as my framework of choice, as a backend I use markdown files stored in a git repo.

To render my files I use React Markdown. It's an awesome component that can render markdown files without any config.

After a few posts, I figured out that I need some custom components rendered with my posts. One was for images, to make them zoomable for phones and the second one was a syntax highlighter for code snippets.

React has a huge amount of components that can help us with both of these. I decided to use react-zoom-pan-pinch for image zoom and react-syntax-highlighter for syntax highlight.

Ok. I have my components, not how to render them from our markdown files. Fortunately, React Markdown has us covered. It allows us to do a custom rendering of some tags.

We can do something like this:

import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { materialDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";

<ReactMarkdown
    components={{
        p: ({ node, children }) => {
            if (node.children[0].tagName === "img") {
                const image = node.children[0];
                return (
                    <TransformWrapper>
                        <TransformComponent>
                            <img
                                alt={image.properties.alt}
                                src={`${image.properties.src}`}
                            />
                        </TransformComponent>
                    </TransformWrapper>
                );
            }
            // Return default child if it's not an image
            return <p>{children}</p>;
        },
        code({ className, children }) {
            // Removing "language-" because React-Markdown already added "language-"
            let language = "";
            if (className) {
                language = className.replace("language-", "");
            }
            return (
                <SyntaxHighlighter
                    style={materialDark}
                    language={language}
                    children={children[0]}
                />
            );
        },
    }}
>
    {postData.contentMarkdown}
</ReactMarkdown>
Enter fullscreen mode Exit fullscreen mode

For images, we must hook into

tag, as ReactMarkdown is wrapping images in paragraphs. So with a simple if we decide if the first child of the paragraph is an image. If so we replace the whole thing.

For code, we are just replacing the whole code tag.


If you like this article you can follow me on Twitter.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .