Published: Apr 22, 2025, 11:02 AM
This guide will walk you through the complete process of fetching Markdown content from a Supabase database and rendering it on the frontend using the react-markdown library in a Next.js (App Router) project.
react-markdown
@supabase/ssr
, @supabase/supabase-js
)react-markdown
(Markdown rendering library)remark-gfm
(GFM support plugin)rehype-highlight
& highlight.js
(optional, for code block syntax highlighting)@tailwindcss/typography
(recommended for Markdown styling)posts
table with at least one text
type column (e.g., content
) for storing raw Markdownnpm install react-markdown remark-gfm rehype-highlight highlight.js
# or
yarn add react-markdown remark-gfm rehype-highlight highlight.js
Required packages:
react-markdown
: Core rendering libraryremark-gfm
: Plugin for GitHub Flavored Markdown supportrehype-highlight
: Plugin for code block syntax highlightinghighlight.js
: Core library required by rehype-highlightChoose from these approaches:
// app/blog/[slug]/page.tsx
import { createClient } from '@/utils/supabase/server';
import { notFound } from 'next/navigation';
async function getPost(slug: string) {
const supabase = createClient();
const { data: post, error } = await supabase
.from('posts')
.select('title, content, published_at')
.eq('slug', slug)
.maybeSingle();
if (error) console.error("Error fetching post:", error);
if (!post) notFound();
return post;
}
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeHighlight from 'rehype-highlight';
export default async function PostPage({ params }) {
const post = await getPost(params.slug);
return (
<article className="prose lg:prose-xl dark:prose-invert">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeHighlight]}
>
{post.content}
</ReactMarkdown>
</article>
);
}