A simple way to integrate markdown file in Next.js

Anh-Thi Dinh
👆
For the official way, read this documentation.
In my case, I don’t like this official one because it’s too heavy where I just need to display some markdown files on the site.
⚠️
Weakness of the method given in this note is the content of markdown file isn’t rendered automatically if the file has changes in the dev mode.

Main idea

As an example, you can check
  1. Markdown files are stored in public folder so that we can fetch() them just use their paths. For example, public/sample.md can be access via /sample.md.
  1. Everything is put inside a "use client" component.
  1. If you want to display a content in a server component, put this client component in it.
  1. Using raw-loader allows Next.js to read the markdown file.
  1. Using react-markdown to render the markdown file.
  1. Bonus: using react-syntax-highlighter to display codes with syntax highlights.

Click to show a content from a markdown file

Want: click the button → show a popup which contains the rendered markdown.
1// Server component
2<ClientComponent />
1// Client
2"use client"
3
4export default ClientComponent() {
5	const [content, setContent] = useState<string | undefined>(undefined)
6
7	const handleClick = async () => {
8		await fetch('/sample.md').then(res => res.text())
9			.then(text => setContent(text))
10	}
11
12	return (
13		<>
14			{content && (
15				<ReactMarkdown components={MarkdownComponents}>
16			    {content}
17			  </ReactMarkdown>
18			)}
19		</>
20	)
21}
1import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
2import { dracula } from 'react-syntax-highlighter/dist/esm/styles/prism'
3
4export const MarkdownComponents: object = {
5  code({ node, className, ...props }: any) {
6    const hasLang = /language-(\w+)/.exec(className || '')
7    return hasLang ? (
8      <SyntaxHighlighter
9        style={dracula}
10        language={hasLang[1]}
11        PreTag="div"
12        className="codeStyle"
13        showLineNumbers={true}
14        useInlineStyles={true}
15      >
16        {props.children}
17      </SyntaxHighlighter>
18    ) : (
19      <code className={className} {...props} />
20    )
21  }
22}

Load a page whose content from a markdown file

Purpose: We want to display a site in src/app/site-name/page.tsx. Its content is taken from public/site-name.md.
1import PageContent from '../../components/PageContent'
2
3export default async function SiteName() {
4  return (
5    <div>
6      <PageContent filePath={'site-name.md'} />
7    </div>
8  )
9}
1'use client'
2
3import { useEffect, useState } from 'react'
4import ReactMarkdown from 'react-markdown'
5import { MarkdownComponents } from '../libs/helpers'
6
7type PageContentProps = {
8  filePath: string
9}
10
11export default function PageContent(props: PageContentProps) {
12  const [content, setContent] = useState<string | undefined>(undefined)
13  useEffect(() => {
14    const fetchContent = async () => {
15      const data = await fetch(props.filePath).then(res => res.text())
16      setContent(data)
17    }
18    fetchContent().catch(console.error)
19  }, [])
20
21  return (
22    <>
23      {content && <ReactMarkdown components={MarkdownComponents}>{content}</ReactMarkdown>}
24    <>
25  )
26}