<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>
<aside> ☝ As an example, you can check https://github.com/dinhanhthi/devboost.app
</aside>
public
folder so that we can fetch()
them just use their paths. For example, public/sample.md
can be access via /sample.md
."use client"
component.raw-loader
allows Next.js to read the markdown file.react-markdown
to render the markdown file.react-syntax-highlighter
to display codes with syntax highlights.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} />
)
}
}
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>}
<>
)
}