cft

Rendering markdown made easy with react-markdown in ReactJS and NextJS web APPs

Rendering Markdown is a kind of mess, but it is fun with react-markdown, learn how to implement React markdown in NextJS


user

Mohammed Nadeem Shareef

a year ago | 8 min read

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

  • I am using NextJS with TypeScript for this blog.
  • I have a folder/directory with the name post containing all the posts and inside the post folder/directory I have two posts.
  • You can find the code on my GitHub (Link will be shared at the end).

// pages/index.tsximport { 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;

  • I am keeping things simple to focus more on React-Markdown

These two posts are taken from Maximilian course on NextJS

  • Then we have a file to show individual posts and render our MARKDOWN

// /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.

  • Install React-Markdwon

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.

  • ReactMarkdown has a components prop which helps us to map tag name to React Component
  • components take an object which contains the mapping of HTML tags.

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

  • Firstly install syntax-highlighter

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

  • react-syntax-highlighter makes it very easy to style our CODE blocks.
  • Here I am using 'materialDark' but you have many options and many themes to apply for your site.

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

Upvote


user
Created by

Mohammed Nadeem Shareef

Frontend Developer

Hello. I am a FrontEnd Developer I work with reactJS, nextJS, TypeScript, firebase, tailwindcss, SCSS.


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles