Test Blog Post
Starter template for writing out a blog post using MDX/JSX and Next.js.
Abdullah Muhammad
Published on October 27, 2025 • 5 min read
Understand Dynamic MDX with Supabase
MDX opens the door to combining Markdown with JSX, making your blog or documentation highly flexible. But what if you don’t want to keep your MDX files in the file system? That’s where Supabase comes in.
In this article, we explore how to store and render MDX content dynamically from Supabase, using next-mdx-remote in a Next.js App Router project.
🧠 Why Store MDX in Supabase?
Storing MDX in Supabase enables:
- Dynamic blog posts
 - Easier authoring through a CMS or dashboard
 - Version control and user-generated content
 - Centralized content API
 
Instead of placing .mdx files in your repo, you fetch them from Supabase and render them as needed.
Working with Images in MDX
The following is an image utilizing the Next.js Image component and the built-in HTML element for working with figcaptions:
🛠️ Storing MDX in Supabase
Create a table named articles in Supabase with columns like:
id: UUIDslug:texttitle:textcontent:text(your raw MDX string)created_at:timestamp
Here’s a sample GitHub Gist for you to view:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verify } from 'jsonwebtoken';
// Simple middleware to protect specific routes
export function middleware(request: NextRequest) {
  // Get token from cookies
  const token = request.cookies.get('token')?.value;
  
  // If no token exists, direct the user to the main page.
  // Send a response redirect
  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  
  try {
    // Verify JWT
    const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
    verify(token, JWT_SECRET);
    
    // If the token is valid, transfer control of request to the appropriate route handler function
    return NextResponse.next();
  } 
  catch (error) {
    // Token is invalid, redirect to login
    return NextResponse.redirect(new URL('/login', request.url));
  }
}
// Only run middleware on protected routes
export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*', '/api/protected/:path*'],
};And here, is a sample code block featuring how to work with SQL
INSERT INTO articles (id, slug, title, content) VALUES (
  uuid_generate_v4(),
  'dynamic-mdx-with-supabase',
  'Understand Dynamic MDX with Supabase',
  '# Hello from Supabase\n\nThis MDX content is stored in the cloud!',
  now()
);