Thi's avatar
HomeAboutNotesBlogTopicsToolsReading
About|My sketches |Cooking |Cafe icon Support Thi
πŸ’Œ [email protected]

A simple way to integrate markdown file in Next.js

Anh-Thi Dinh
Next.jsSSGReact
Left aside
πŸ‘†
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
devboost.app
  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.

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.
About|My sketches |Cooking |Cafe icon Support Thi
πŸ’Œ [email protected]
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}
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}