Skip to main content

How to Render Markdown with React

Objectives

Documentation is a critical aspect of learning and development. To store and organize the knowledge I've gained during my tech journey, I want to create a documentation website. Since Markdown is a simple and efficient way to write documentation, I decided to:

  • Take notes on processes in Markdown files locally.
  • Render and view these files in a React app online.

After conducting research, I found two main approaches to achieve this:

  1. Using .mdx files: These files allow you to combine Markdown content and React components in the same file.
  2. Loading predefined .md files: In this approach, you load standard Markdown files into a component and render them.

Approach 1: use MDX

Sources

gg gg

To display code block refer to these sources

  1. Mdxjs - SyntaxHighlighter
  2. React-syntax-highlighter
  3. SyntaxHighlighter - sample code
  4. SyntaxHighlighter tutorial
  5. mdx v2
  6. Markdown - tutorial
  7. remark-gfm
  8. remark-gfm-repo
  9. react-markdown

Step 1: Install dependencies

npm @mdx-js/react @mdx-js/rollup
npm i --save-dev @types/react-syntax-highlighter

Step 2 (Optional): Configure Vite

If you created your React project with Vite, update your vite.config.js file to include the MDX Rollup plugin:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import mdx from "@mdx-js/rollup";

export default defineConfig({
plugins: [
react(),
mdx(),
],
});

Step 3: MDX Component

Below is an example of a component that takes in .mdx file path to render.

import React, { Suspense, FC } from 'react';
import { MDXComponents } from 'mdx/types.js';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { dark } from 'react-syntax-highlighter/dist/esm/styles/prism';

const components: MDXComponents = {
h1: (props) => <h1 style={{ color: 'blue', fontSize: '2rem', textAlign: 'center' }} {...props} />,
code: ({ className, children, ...props }) => {
const match = /language-(\w+)/.exec(className || '');
return match ? (
<SyntaxHighlighter style={dark} language={match[1]}>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
};

interface StyledMDXProps {
filePath: string;
}

const StyledMDX: FC<StyledMDXProps> = ({ filePath }) => {
const Example = React.lazy(() => import(filePath));
return (
<Suspense fallback={<div>Loading...</div>}>
<Example components={components} />
</Suspense>
);
};

export default StyledMDX;

Approach 2: Loading .md files

Step 1: Install dependencies

npm install react-markdown remark-gfm  

Step 2: MD Component

Below is an example of a component that takes in .md file path to render.

import { useEffect, useState, type FC } from 'react';
import ReactMarkdown, { Components } from "react-markdown";
import remarkGfm from 'remark-gfm';
import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'
import {dracula} from 'react-syntax-highlighter/dist/esm/styles/prism';

interface MarkdownLayoutProps {
filePath: string;
}

const MarkdownLayout: FC<MarkdownLayoutProps> = ({filePath}) => {
const [markdown, setMarkdown] = useState<string>("");

useEffect(() =>{
fetch(filePath)
.then((response) => response.text())
.then((text) => setMarkdown(text))
.catch((error) => console.error("Error loading markdown:", error))
}, [filePath])

const components: Components = {
code: ({ className, children, ...props }) => {
const match = /language-(\w+)/.exec(className || '');
return match ? (
<SyntaxHighlighter
style={dracula}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
<>
<p>HERE</p>
<code className={className} {...props}>
{children}
</code>
</>
);
},
};

return (
<div>
<ReactMarkdown remarkPlugins={[remarkGfm]}
components={components}
>
{markdown}
</ReactMarkdown>
</div>
);
}

export default MarkdownLayout;