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:
- Using
.mdx
files: These files allow you to combine Markdown content and React components in the same file. - Loading predefined
.md
files: In this approach, you load standard Markdown files into a component and render them.
Approach 1: use MDX
Sources
To display code block refer to these sources
- Mdxjs - SyntaxHighlighter
- React-syntax-highlighter
- SyntaxHighlighter - sample code
- SyntaxHighlighter tutorial
- mdx v2
- Markdown - tutorial
- remark-gfm
- remark-gfm-repo
- 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;