<aside> 👆 For the official way, read this documentation.

</aside>

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.

<aside> ⚠️ 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.

</aside>

Main idea

<aside> ☝ As an example, you can check https://github.com/dinhanhthi/devboost.app

</aside>

  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.
  2. Everything is put inside a "use client" component.
  3. If you want to display a content in a server component, put this client component in it.
  4. Using raw-loader allows Next.js to read the markdown file.
  5. Using react-markdown to render the markdown file.
  6. 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.

// Server component
<ClientComponent />
// Client
"use client"

export default ClientComponent() {
	const [content, setContent] = useState<string | undefined>(undefined)

	const handleClick = async () => {
		await fetch('/sample.md').then(res => res.text())
			.then(text => setContent(text))
	}

	return (
		<>
			{content && (
				<ReactMarkdown components={MarkdownComponents}>
			    {content}
			  </ReactMarkdown>
			)}
		</>
	)
}
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
import { dracula } from 'react-syntax-highlighter/dist/esm/styles/prism'

export const MarkdownComponents: object = {
  code({ node, className, ...props }: any) {
    const hasLang = /language-(\\w+)/.exec(className || '')
    return hasLang ? (
      <SyntaxHighlighter
        style={dracula}
        language={hasLang[1]}
        PreTag="div"
        className="codeStyle"
        showLineNumbers={true}
        useInlineStyles={true}
      >
        {props.children}
      </SyntaxHighlighter>
    ) : (
      <code className={className} {...props} />
    )
  }
}

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.

import PageContent from '../../components/PageContent'

export default async function SiteName() {
  return (
    <div>
      <PageContent filePath={'site-name.md'} />
    </div>
  )
}
'use client'

import { useEffect, useState } from 'react'
import ReactMarkdown from 'react-markdown'
import { MarkdownComponents } from '../libs/helpers'

type PageContentProps = {
  filePath: string
}

export default function PageContent(props: PageContentProps) {
  const [content, setContent] = useState<string | undefined>(undefined)
  useEffect(() => {
    const fetchContent = async () => {
      const data = await fetch(props.filePath).then(res => res.text())
      setContent(data)
    }
    fetchContent().catch(console.error)
  }, [])

  return (
    <>
      {content && <ReactMarkdown components={MarkdownComponents}>{content}</ReactMarkdown>}
    <>
  )
}