Blog Image

Rendering markdown made easy with react-markdown

Nadeem Shareef | Fri May 14 2021

Hello Developers 👋

If you are using ReactJS or NextJS(React Framework) to make your blog then rendering MARKDOWN is so much easy with React-Markdown

React-Markdown comes with build-in TypeScript support 🤩. Let's stop talking and start coding.

Basic setup

// pages/index.tsx
import { FC, Fragment } from "react";
import Link from "next/link";

interface Props {}

const Home: FC<Props> = () => (
  <Fragment>
    <h1>Hello Next.js 👋</h1>
    <div className="posts-container">
      <Link href="/posts/first-post">First post</Link>
      <Link href="/posts/second-post">Second post</Link>
    </div>
  </Fragment>
);

export default Home;

These two posts are taken from Maximilian course on NextJS

// /pages/posts/[slug].tsx

import { FC, Fragment } from "react";
import { GetStaticProps, GetStaticPropsContext, GetStaticPaths } from "next";
import { PostType } from "../../interfaces";
import { getPostData, getPostsFiles } from "../../lib/post-utils";
import PostContent from "../../components/PostContent";

interface Props {
  post: PostType;
}

const BlogPost: FC<Props> = ({ post }: Props) => {
  return (
    <Fragment>
      // /components/PostContent.tsx
      <PostContent post={post} />
    </Fragment>
  );
};

export const getStaticProps: GetStaticProps = async (
  context: GetStaticPropsContext
) => {
  const { slug } = context.params;
  const postData = getPostData(slug);
  return {
    props: {
      post: postData,
    },
    // regenerate after every 600s(10mins)
    revalidate: 600,
  };
};

export const getStaticPaths: GetStaticPaths = async () => {
  const postFilenames = getPostsFiles();
  const slugs = postFilenames.map((fileName) => fileName.replace(/\.md$/, ""));
  return {
    paths: slugs.map((slug) => ({ params: { slug: slug } })),
    fallback: false,
  };
};

export default BlogPost;

Here I set up the basic /posts/[slug] page, I will not go into details, let's Work on the PostContent component

Let's work with React-Markdown.

     npm i react-markdown

Now the easiest way to render MARKDOWN

import React from "react";
import { PostType } from "../interfaces";
import ReactMarkdown from "react-markdown";

interface Props {
  post: PostType;
}

const PostContent = ({ post }: Props) => {
  return (
    <article className="content">
      <ReactMarkdown>{post.content}</ReactMarkdown>
    </article>
  );
};

export default PostContent;

Wooowww! with just one line we render MARKDOWN But if we go to the first blog it doesn't show an image because we get only the name of the file, we have to generate the path for the image and we can also customize how an image should display.

Let's optimize & customize the image.

Here, we are checking if a P tag has an image element if yes, then we are rendering our own image with our custom styles and if it is not an image then we are just returning the content of it.


...
const PostContent = ({ post }: Props) => {
    return (
        <article className="content">
            <ReactMarkdown
                components={{
                    p: ({ node, children }) => {
                        if (node.children[0].tagName === "img") {
                            const image: any = node.children[0];
                            return (
                                <div className="image">
                                    <Image
                                        src={`/images/${image.properties.src}`}
                                        alt={image.properties.alt}
                                        width="600"
                                        height="300"
                                    />
                                </div>
                            );
                        }
                        // Return default child if it's not an image
                        return <p>{children}</p>;
                    },
                }}
            >
                {post.content}
            </ReactMarkdown>
        </article>
    );
};

Let's add syntax highlighting for CODE blocks

npm i react-syntax-highlighter @types/react-syntax-highlighter

import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { materialDark } from "react-syntax-highlighter/dist/cjs/styles/prism";

const PostContent = ({ post }: Props) => {
    return (
        <article className="content">
            <ReactMarkdown
                components={{
                    p: ({ node, children }) => {
                        ...
                    },
                    code({ className, children }) {
                        // Removing "language-" because React-Markdown already added "language-"
                        const language = className.replace("language-", "");
                        return (
                            <SyntaxHighlighter
                                style={materialDark}
                                language={language}
                                children={children[0]}
                            />
                        );
                    },
                }}
            >
                {post.content}
            </ReactMarkdown>
        </article>
    );
};

Closing here 👋👋👋

This is Shareef. My Portfolio Twitter ShareefBhai99 > GitHub repo of this blog > Linkedin My other Blogs

Cover photo by [Dustin Curtis] (https://github.com/dcurtis/markdown-mark/tree/master/svg)