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.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;
- 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 components prop which help 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
Cover photo by [Dustin Curtis] (https://github.com/dcurtis/markdown-mark/tree/master/svg)