<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged next-mdx-remote-client</title>
<link>https://coderlegion.com/tag/next-mdx-remote-client</link>
<description>Powered by Question2Answer</description>
<item>
<title>next-mdx-remote-client Guide for Next.js 16 (2026)</title>
<link>https://coderlegion.com/25579/next-mdx-remote-client-guide-for-next-js-16-2026</link>
<description>&lt;p&gt;If you followed &lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/mdx-remote-nextjs-16-guide&quot;&gt;our original MDX blog setup guide&lt;/a&gt;, you already have a working blog powered by &lt;code&gt;next-mdx-remote&lt;/code&gt;. There's just one problem: HashiCorp archived that repo on April 9, 2026. The package still runs fine today — nothing breaks overnight — but it's now frozen. No new features, no security patches, no fixes if a future Next.js or React release changes something it depends on.&lt;/p&gt;
&lt;p&gt;That's not a reason to panic, but it is a reason to plan your next move. The community's answer is &lt;strong&gt;&lt;code&gt;next-mdx-remote-client&lt;/code&gt;&lt;/strong&gt; — an actively maintained fork that keeps the same mental model (compile MDX on the server, render it as React) while fixing several rough edges the original never got around to, including a proper built-in error boundary and support for &lt;code&gt;export&lt;/code&gt; statements inside MDX on the App Router.&lt;/p&gt;
&lt;p&gt;This guide walks through the same MDX blog setup as before, rebuilt from scratch on &lt;code&gt;next-mdx-remote-client&lt;/code&gt; — content folder, typed data layer, custom components, syntax highlighting, and dynamic SEO metadata — so you can migrate confidently or start fresh if you're building a new Next.js 16 blog today.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Note: This guide targets the &lt;strong&gt;App Router&lt;/strong&gt; using &lt;code&gt;next-mdx-remote-client&lt;/code&gt;'s React Server Component API (the &lt;code&gt;/rsc&lt;/code&gt; subpath). If you're on the Pages Router, the package has a separate, isolated API for that too — see the &lt;a rel=&quot;nofollow&quot; href=&quot;#page-router-faq&quot;&gt;FAQ&lt;/a&gt; at the end.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-next-mdx-remote-client-instead-of-next-mdx-remote&quot;&gt;Why next-mdx-remote-client Instead of next-mdx-remote?&lt;/h2&gt;
&lt;p&gt;Both packages solve the same problem: MDX content stored &lt;em&gt;outside&lt;/em&gt; your &lt;code&gt;app&lt;/code&gt; folder — a &lt;code&gt;content/blogs&lt;/code&gt; directory, a database, a CMS — rendered through one dynamic &lt;code&gt;[slug]&lt;/code&gt; route, instead of every post being its own route file under &lt;code&gt;@next/mdx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;While the original &lt;code&gt;next-mdx-remote&lt;/code&gt; was the long-time standard for this pattern, its archiving means developers need a modern alternative. &lt;code&gt;next-mdx-remote-client&lt;/code&gt; has emerged as the community's go-to, actively maintained drop-in replacement built specifically for the App Router. A few concrete differences that actually matter day to day:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Feature &lt;/th&gt;&lt;th&gt;&lt;code&gt;next-mdx-remote&lt;/code&gt; &lt;/th&gt;&lt;th&gt;&lt;code&gt;next-mdx-remote-client&lt;/code&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Actively maintained &lt;/td&gt;&lt;td&gt;❌ Archived April 2026 &lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Built-in error handling in App Router &lt;/td&gt;&lt;td&gt;❌ You wrap it yourself &lt;/td&gt;&lt;td&gt;✅ &lt;code&gt;onError&lt;/code&gt; prop&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;export&lt;/code&gt; statements from MDX in App Router &lt;/td&gt;&lt;td&gt;❌ &lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Get frontmatter + scope without a separate parse step &lt;/td&gt;&lt;td&gt;❌ &lt;/td&gt;&lt;td&gt;✅ (&lt;code&gt;evaluate&lt;/code&gt; function)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Utility to read frontmatter without compiling &lt;/td&gt;&lt;td&gt;❌ &lt;/td&gt;&lt;td&gt;✅ &lt;code&gt;getFrontmatter&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;MDX version &lt;/td&gt;&lt;td&gt;3 &lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;What &quot;built-in error handling&quot; actually buys you:&lt;/strong&gt; with &lt;code&gt;next-mdx-remote&lt;/code&gt;, a broken MDX file (a stray &lt;code&gt;{&lt;/code&gt; in an expression, an unclosed JSX tag) throws during render, and it's on you to catch it with an &lt;code&gt;ErrorBoundary&lt;/code&gt;. &lt;code&gt;next-mdx-remote-client&lt;/code&gt;'s &lt;code&gt;MDXRemote&lt;/code&gt; component takes an &lt;code&gt;onError&lt;/code&gt; prop directly, so a bad post shows a friendly fallback instead of crashing the whole page — which matters a lot the first time a typo in frontmatter takes down your blog in production.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;next-mdx-remote-client&lt;/code&gt; requires &lt;strong&gt;React 19.1+&lt;/strong&gt; and is tested against &lt;strong&gt;Next.js 15 and 16&lt;/strong&gt;. If your project is still on React 18, install the &lt;code&gt;@^1&lt;/code&gt; tag instead — see &lt;a rel=&quot;nofollow&quot; href=&quot;#step-1-install-dependencies&quot;&gt;Step 1&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-quick-word-on-security&quot;&gt;A Quick Word on Security&lt;/h3&gt;
&lt;p&gt;Before writing any code: MDX compiles to executable JavaScript. That's what makes &lt;code&gt;&amp;lt;Component /&amp;gt;&lt;/code&gt; tags work inside your Markdown, but it also means rendering MDX from a source you don't fully control (user submissions, an open CMS) can enable XSS or even remote code execution — this is true of every MDX renderer, not something specific to this package.&lt;/p&gt;
&lt;p&gt;For a personal or team blog where you're the only one writing &lt;code&gt;.mdx&lt;/code&gt; files, this isn't a practical concern — you already trust your own content. If you ever accept MDX from outside contributors, strip dangerous expressions at the remark stage first (the maintainer's own &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ipikuka/remark-mdx-remove-expressions&quot;&gt;&lt;code&gt;remark-mdx-remove-expressions&lt;/code&gt;&lt;/a&gt; plugin is built for exactly this) rather than trying to sanitize after the fact.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;This guide uses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Next.js 16&lt;/strong&gt; with the App Router&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React 19.2+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TypeScript&lt;/strong&gt; in strict mode&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node.js 20.9+&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We'll build:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MDX files stored in a &lt;code&gt;content/blogs&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;A reusable &lt;code&gt;getAllPosts()&lt;/code&gt; / &lt;code&gt;getPostBySlug()&lt;/code&gt; data layer&lt;/li&gt;
&lt;li&gt;Custom-styled MDX components (headings, code blocks, links, tables)&lt;/li&gt;
&lt;li&gt;Syntax highlighting with &lt;code&gt;sugar-high&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;remark-gfm&lt;/code&gt; for GitHub-flavored Markdown (tables, strikethrough, task lists)&lt;/li&gt;
&lt;li&gt;Built-in error handling via &lt;code&gt;onError&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Dynamic SEO metadata and JSON-LD structured data per post&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-1-install-dependencies&quot;&gt;Step 1: Install Dependencies&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install next-mdx-remote-client gray-matter remark-gfm reading-time sugar-high
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since when this matters is the whole point of this migration, be explicit about which major version you need:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# React 19 projects (the default for new Next.js 16 apps)
npm install next-mdx-remote-client@^2

# React 18 projects
npm install next-mdx-remote-client@^1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What each package does:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Package &lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;next-mdx-remote-client&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Compiles and renders MDX strings as React, with built-in error handling&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;gray-matter&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Parses frontmatter for the post-listing data layer&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;remark-gfm&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Adds GitHub-flavored Markdown — tables, strikethrough, task lists&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;reading-time&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Calculates estimated reading time from post content&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;sugar-high&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Lightweight, zero-client-JS syntax highlighter for code blocks&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;Common Mistake:&lt;/strong&gt; Importing from &lt;code&gt;next-mdx-remote-client&lt;/code&gt; directly. The package splits its App Router and Pages Router code into &lt;strong&gt;separate, isolated subpaths&lt;/strong&gt; — for the App Router you always import from &lt;code&gt;next-mdx-remote-client/rsc&lt;/code&gt;, never the bare package name. We'll use that subpath throughout this guide.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-2-content-folder-and-frontmatter-unchanged-&quot;&gt;Step 2: Content Folder and Frontmatter (Unchanged)&lt;/h3&gt;
&lt;p&gt;If you're migrating from the original guide, this part doesn't change at all — &lt;code&gt;next-mdx-remote-client&lt;/code&gt; reads the same MDX files.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;content/blogs/hello-world.mdx
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;---
title: &quot;Hello World: My First MDX Post&quot;
description: &quot;A short example post to test out the next-mdx-remote-client setup.&quot;
publishedAt: &quot;2026-08-20&quot;
tags: [&quot;MDX&quot;, &quot;Next.js&quot;]
featured: false
---

## Hello World

This is my **first** MDX post rendered with `next-mdx-remote-client`.

- It supports Markdown
- It supports *React components*
- It even supports tables (thanks to remark-gfm)

| Feature            | Supported |
| ------------------ | --------- |
| Tables             | ✅       |
| Code blocks        | ✅       |
| Custom components  | ✅       |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keep frontmatter field names consistent across every post (&lt;code&gt;title&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;publishedAt&lt;/code&gt;, &lt;code&gt;tags&lt;/code&gt;). A typo like &lt;code&gt;date&lt;/code&gt; instead of &lt;code&gt;publishedAt&lt;/code&gt; on one file will silently break sorting for that post, since your data layer expects an exact key.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-3-build-the-data-layer&quot;&gt;Step 3: Build the Data Layer&lt;/h2&gt;
&lt;p&gt;This file reads every &lt;code&gt;.mdx&lt;/code&gt; file from disk, parses frontmatter with &lt;code&gt;gray-matter&lt;/code&gt;, and returns typed post objects for your listing page. This part still uses &lt;code&gt;gray-matter&lt;/code&gt; on purpose — it's what powers the blog index (&lt;code&gt;getAllPosts&lt;/code&gt;), which needs frontmatter &lt;em&gt;without&lt;/em&gt; compiling MDX for every post just to render a list.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// lib/posts.ts
import fs from &quot;fs&quot;;
import path from &quot;path&quot;;
import matter from &quot;gray-matter&quot;;
import readingTime from &quot;reading-time&quot;;

const POSTS_PATH = path.join(process.cwd(), &quot;content/blogs&quot;);

export type Post = {
  slug: string;
  title: string;
  description: string;
  publishedAt: string;
  updatedAt?: string;
  tags: string[];
  readingTime: string;
  content: string;
};

export function getAllPosts(): Post[] {
  const files = fs.readdirSync(POSTS_PATH);

  const posts = files.map((file) =&amp;gt; {
    const filePath = path.join(POSTS_PATH, file);
    const fileContent = fs.readFileSync(filePath, &quot;utf-8&quot;);

    const { data, content } = matter(fileContent);

    const slug = file.replace(/\.mdx$/, &quot;&quot;);

    return {
      slug,
      title: data.title,
      description: data.description,
      publishedAt: data.publishedAt,
      updatedAt: data.updatedAt,
      tags: data.tags || [],
      readingTime: readingTime(content).text,
      content,
    };
  });

  return posts.sort(
    (a, b) =&amp;gt;
      new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime()
  );
}

export function getPostBySlug(slug: string) {
  return getAllPosts().find((post) =&amp;gt; post.slug === slug);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;💡 &lt;strong&gt;Tip — an alternative worth knowing:&lt;/strong&gt; &lt;code&gt;next-mdx-remote-client&lt;/code&gt; ships its own &lt;code&gt;getFrontmatter&lt;/code&gt; utility that reads frontmatter &lt;strong&gt;without&lt;/strong&gt; compiling the MDX, using the same &lt;code&gt;vfile-matter&lt;/code&gt; extractor internally:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { getFrontmatter } from &quot;next-mdx-remote-client/utils&quot;;

const { frontmatter, strippedSource } = getFrontmatter&amp;lt;Post&amp;gt;(fileContent);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's isolated in its own subpath and costs almost nothing to import, so it's a reasonable way to drop the &lt;code&gt;gray-matter&lt;/code&gt; dependency entirely if you're starting fresh. This guide keeps &lt;code&gt;gray-matter&lt;/code&gt; since it's already a common dependency in most Next.js content setups and behaves identically either way.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;lib/posts.ts&lt;/code&gt; uses Node's &lt;code&gt;fs&lt;/code&gt; module, so it only runs on the server — inside Server Components, &lt;code&gt;generateStaticParams&lt;/code&gt;, or &lt;code&gt;generateMetadata&lt;/code&gt;. Never import it into a &lt;code&gt;&quot;use client&quot;&lt;/code&gt; component.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-4-create-custom-mdx-components&quot;&gt;Step 4: Create Custom MDX Components&lt;/h3&gt;
&lt;p&gt;The component map itself is unchanged from a &lt;code&gt;next-mdx-remote&lt;/code&gt; setup — this is standard &lt;code&gt;@mdx-js/mdx&lt;/code&gt; behavior, and &lt;code&gt;next-mdx-remote-client&lt;/code&gt; uses the same convention.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;// mdx-components.tsx
import React, { ComponentPropsWithoutRef } from &quot;react&quot;;
import Link from &quot;next/link&quot;;
import { highlight } from &quot;sugar-high&quot;;
import type { MDXComponents } from &quot;next-mdx-remote-client/rsc&quot;;

type HeadingProps = ComponentPropsWithoutRef&amp;lt;&quot;h1&quot;&amp;gt;;
type ParagraphProps = ComponentPropsWithoutRef&amp;lt;&quot;p&quot;&amp;gt;;
type ListProps = ComponentPropsWithoutRef&amp;lt;&quot;ul&quot;&amp;gt;;
type AnchorProps = ComponentPropsWithoutRef&amp;lt;&quot;a&quot;&amp;gt;;
type BlockquoteProps = ComponentPropsWithoutRef&amp;lt;&quot;blockquote&quot;&amp;gt;;

export const mdxComponents: MDXComponents = {
  h1: (props: HeadingProps) =&amp;gt; (
    &amp;lt;h1 className=&quot;scroll-m-20 text-4xl font-bold tracking-tight lg:text-5xl&quot; {...props} /&amp;gt;
  ),
  h2: (props: HeadingProps) =&amp;gt; (
    &amp;lt;h2 className=&quot;scroll-m-20 mt-16 pb-2 text-3xl font-semibold tracking-tight&quot; {...props} /&amp;gt;
  ),
  h3: (props: HeadingProps) =&amp;gt; (
    &amp;lt;h3 className=&quot;scroll-m-20 mt-12 text-2xl font-semibold tracking-tight&quot; {...props} /&amp;gt;
  ),
  p: (props: ParagraphProps) =&amp;gt; (
    &amp;lt;p className=&quot;text-gray-800 dark:text-zinc-300&quot; {...props} /&amp;gt;
  ),
  ul: (props: ListProps) =&amp;gt; (
    &amp;lt;ul className=&quot;text-gray-800 dark:text-zinc-300 list-disc pl-5 space-y-1&quot; {...props} /&amp;gt;
  ),
  a: ({ href, children, ...props }: AnchorProps) =&amp;gt; {
    const className = &quot;text-blue-500 hover:text-blue-700&quot;;

    if (href?.startsWith(&quot;/&quot;)) {
      return (
        &amp;lt;Link href={href} className={className} {...props}&amp;gt;
          {children}
        &amp;lt;/Link&amp;gt;
      );
    }

    return (
      &amp;lt;a href={href} target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; className={className} {...props}&amp;gt;
        {children}
      &amp;lt;/a&amp;gt;
    );
  },
  code: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;&quot;code&quot;&amp;gt;) =&amp;gt; {
    const codeHTML = highlight(String(children));
    return &amp;lt;code dangerouslySetInnerHTML={{ __html: codeHTML }} {...props} /&amp;gt;;
  },
  blockquote: (props: BlockquoteProps) =&amp;gt; (
    &amp;lt;blockquote className=&quot;ml-[0.075em] border-l-3 border-green-400 pl-4 text-gray-700&quot; {...props} /&amp;gt;
  ),
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What's different here from a &lt;code&gt;next-mdx-remote&lt;/code&gt; setup:&lt;/strong&gt; the &lt;code&gt;MDXComponents&lt;/code&gt; type now comes from &lt;code&gt;next-mdx-remote-client/rsc&lt;/code&gt; instead of &lt;code&gt;mdx/types&lt;/code&gt; — the package re-exports it so you don't need a separate import. Everything else, including the special &lt;code&gt;wrapper&lt;/code&gt; key if you want to wrap MDX content in a container element, works exactly the same way.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-5-build-the-dynamic-blog-route&quot;&gt;Step 5: Build the Dynamic Blog Route&lt;/h3&gt;
&lt;p&gt;This is where the actual migration happens. Swap the import source and add the &lt;code&gt;onError&lt;/code&gt; prop — the rest of the route looks familiar if you've built this with &lt;code&gt;next-mdx-remote&lt;/code&gt; before.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;// app/blog/[slug]/page.tsx
import { Suspense } from &quot;react&quot;;
import { notFound } from &quot;next/navigation&quot;;
import { MDXRemote, type MDXRemoteOptions } from &quot;next-mdx-remote-client/rsc&quot;;
import remarkGfm from &quot;remark-gfm&quot;;
import { getAllPosts, getPostBySlug } from &quot;@/lib/posts&quot;;
import { mdxComponents } from &quot;@/mdx-components&quot;;
import ErrorComponent from &quot;@/components/error-component&quot;;

interface BlogPostPageProps {
  params: Promise&amp;lt;{ slug: string }&amp;gt;;
}

// Pre-renders every post at build time (Static Site Generation)
export async function generateStaticParams() {
  const posts = getAllPosts();
  return posts.map((post) =&amp;gt; ({ slug: post.slug }));
}

export async function generateMetadata({ params }: BlogPostPageProps) {
  const { slug } = await params;
  const post = getPostBySlug(slug);

  if (!post) return {};

  return {
    title: post.title,
    description: post.description,
  };
}

const mdxOptions: MDXRemoteOptions = {
  mdxOptions: {
    remarkPlugins: [remarkGfm],
  },
};

export default async function BlogPostPage({ params }: BlogPostPageProps) {
  const { slug } = await params;
  const post = getPostBySlug(slug);

  if (!post) {
    notFound();
  }

  return (
    &amp;lt;article className=&quot;prose dark:prose-invert mx-auto&quot;&amp;gt;
      &amp;lt;h1&amp;gt;{post.title}&amp;lt;/h1&amp;gt;

      &amp;lt;Suspense fallback={&amp;lt;p&amp;gt;Loading post…&amp;lt;/p&amp;gt;}&amp;gt;
        &amp;lt;MDXRemote
          source={post.content}
          components={mdxComponents}
          options={mdxOptions}
          onError={ErrorComponent}
        /&amp;gt;
      &amp;lt;/Suspense&amp;gt;
    &amp;lt;/article&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create Error component&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;// components/error-component.tsx

'use client'

export default function ErrorComponent({ error }: { error: unknown | Error }) {
    return (
        &amp;lt;div className=&quot;rounded-xl border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive&quot;&amp;gt;
            &amp;lt;pre&amp;gt;
                &amp;lt;code&amp;gt;
                    {typeof error === &quot;string&quot;
                        ? error
                        : error instanceof Error
                            ? error.message
                            : String(error)}
                &amp;lt;/code&amp;gt;
            &amp;lt;/pre&amp;gt;
        &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What's actually new compared to &lt;code&gt;next-mdx-remote&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;import { MDXRemote } from &quot;next-mdx-remote-client/rsc&quot;&lt;/code&gt;&lt;/strong&gt; — the App Router entry point. Importing from the bare package name here would pull in the Pages Router code path instead, which won't work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; is required, not optional.&lt;/strong&gt; &lt;code&gt;MDXRemote&lt;/code&gt; is an &lt;code&gt;async&lt;/code&gt; Server Component under the hood — it returns a &lt;code&gt;Promise&amp;lt;React.JSX.Element&amp;gt;&lt;/code&gt; — so it needs a Suspense boundary the same way any other async Server Component does.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;onError={ErrorComponent}&lt;/code&gt;&lt;/strong&gt; is the headline feature from &lt;a rel=&quot;nofollow&quot; href=&quot;#why-next-mdx-remote-client-instead-of-next-mdx-remote&quot;&gt;earlier&lt;/a&gt;. If this specific post's MDX fails to &lt;strong&gt;compile&lt;/strong&gt; — a syntax error like an unclosed tag, or a bad frontmatter parse — &lt;code&gt;MDXRemote&lt;/code&gt; catches it internally and renders &lt;code&gt;ErrorComponent&lt;/code&gt; in its place, instead of taking down the whole page.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;options.mdxOptions.remarkPlugins&lt;/code&gt;&lt;/strong&gt; still works exactly the way &lt;code&gt;next-mdx-remote&lt;/code&gt; did — &lt;code&gt;remark-gfm&lt;/code&gt; plugs in the same way for tables and GFM syntax.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;Common Mistake:&lt;/strong&gt; Forgetting the &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; wrapper. Since &lt;code&gt;MDXRemote&lt;/code&gt; is async, rendering it directly without a Suspense boundary will throw a build error telling you an async component needs one — this is a hard requirement of the package, not a style suggestion.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; Notice &lt;code&gt;onError&lt;/code&gt; catches compile/render errors caught by the package's internal handling, but the README is explicit that &lt;strong&gt;evaluating the compiled module itself doesn't throw except syntax errors&lt;/strong&gt; — some render-time errors from a broken custom component can still escape and need a real React &lt;code&gt;&amp;lt;ErrorBoundary&amp;gt;&lt;/code&gt; around your route if you want to be fully defensive. For a typical blog where you control every MDX file, &lt;code&gt;onError&lt;/code&gt; alone covers the realistic failure cases (a bad frontmatter reference, a malformed table, an unclosed tag).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you want to deep dive into &lt;strong&gt;Error Handling in Next.js&lt;/strong&gt;, I've full guide covering this topic &lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/error-handling-in-nextjs-16&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-6-want-frontmatter-and-scope-inside-jsx-use-evaluate-instead&quot;&gt;Step 6: Want Frontmatter and Scope Inside JSX? Use &lt;code&gt;evaluate&lt;/code&gt; Instead&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;MDXRemote&lt;/code&gt; component above is enough if you only need frontmatter and custom data &lt;em&gt;inside&lt;/em&gt; the MDX body (e.g. &lt;code&gt;# {frontmatter.title}&lt;/code&gt; written directly in the &lt;code&gt;.mdx&lt;/code&gt; file). If you need that data &lt;strong&gt;outside&lt;/strong&gt; the MDX, in your page's own JSX — for a byline, a reading-time badge, a table of contents component — reach for the lower-level &lt;code&gt;evaluate&lt;/code&gt; function instead.&lt;/p&gt;
&lt;p&gt;Update &lt;code&gt;lib/posts.ts&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;// lib/posts.ts
export const getSource = async (filename: string): Promise&amp;lt;string | undefined&amp;gt; =&amp;gt; {
    const sourcePath = path.join(POSTS_PATH, filename);
    if (!fs.existsSync(sourcePath)) return;
    return await fs.promises.readFile(sourcePath, &quot;utf8&quot;);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;// app/blog/[slug]/page.tsx (alternative using evaluate)
import { Suspense } from &quot;react&quot;;
import { evaluate, type EvaluateOptions } from &quot;next-mdx-remote-client/rsc&quot;;
import remarkGfm from &quot;remark-gfm&quot;;
import { getSource } from &quot;@/lib/posts&quot;;
import { mdxComponents } from &quot;@/mdx-components&quot;;
import ErrorComponent from &quot;@/components/error-component&quot;;

interface BlogPostPageProps {
    params: Promise&amp;lt;{ slug: string }&amp;gt;;
}

type Frontmatter = {
  title: string;
  description: string;
};

const mdxOptions: MDXRemoteOptions = {
    parseFrontmatter: true,
    mdxOptions: {
        remarkPlugins: [remarkGfm],
    },
};

export default async function BlogPostPage({ params }: BlogPostPageProps) {
    const { slug } = await params;
    const source = await getSource(`${slug}.mdx`)!;

    if (!source) return &amp;lt;ErrorComponent error=&quot;The source could not found!&quot; /&amp;gt;;

  const { content, frontmatter, error } = await evaluate&amp;lt;Frontmatter&amp;gt;({
    source,
    options: mdxOptions,
    components: mdxComponents,
  });

    if (error) return &amp;lt;ErrorComponent error={error} /&amp;gt;;

  return (
    &amp;lt;article className=&quot;prose dark:prose-invert mx-auto&quot;&amp;gt;
      &amp;lt;h1&amp;gt;{frontmatter.title}&amp;lt;/h1&amp;gt;
            &amp;lt;Suspense fallback={&amp;lt;p&amp;gt;Loading post…&amp;lt;/p&amp;gt;}&amp;gt;
                {content}
            &amp;lt;/Suspense&amp;gt;
    &amp;lt;/article&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What's happening here:&lt;/strong&gt; &lt;code&gt;evaluate&lt;/code&gt; returns &lt;code&gt;{ content, mod, frontmatter, scope, error }&lt;/code&gt; all at once — &lt;code&gt;content&lt;/code&gt; is the compiled MDX JSX (still needs its own &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;), &lt;code&gt;frontmatter&lt;/code&gt; is typed and available immediately for use in your surrounding JSX, and &lt;code&gt;error&lt;/code&gt; is a plain object you check yourself instead of a component prop. This is the tool to reach for whenever you need frontmatter or a table-of-contents object &lt;em&gt;before&lt;/em&gt; the MDX renders, not just inside it — the &lt;code&gt;MDXRemote&lt;/code&gt; component genuinely can't do this on the App Router, since it doesn't hand you the frontmatter as a return value at all.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; Passing &lt;code&gt;parseFrontmatter: true&lt;/code&gt; here means &lt;code&gt;evaluate&lt;/code&gt; extracts frontmatter itself — you could skip &lt;code&gt;gray-matter&lt;/code&gt; entirely for a page that only needs a single post. This guide's &lt;code&gt;lib/posts.ts&lt;/code&gt; still uses &lt;code&gt;gray-matter&lt;/code&gt; separately because the blog &lt;strong&gt;index&lt;/strong&gt; page needs frontmatter for every post at once, without compiling every post's MDX just to list titles.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-7-add-json-ld-structured-data-optional-but-great-for-seo-&quot;&gt;Step 7: Add JSON-LD Structured Data (Optional, but Great for SEO)&lt;/h3&gt;
&lt;p&gt;Unchanged from the original setup — this is a Next.js &lt;code&gt;&amp;lt;Script&amp;gt;&lt;/code&gt; tag, not something the MDX package touches.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;// app/blog/[slug]/page.tsx (addition)
import Script from &quot;next/script&quot;;

&amp;lt;Script
  type=&quot;application/ld+json&quot;
  dangerouslySetInnerHTML={{
    __html: JSON.stringify({
      &quot;@context&quot;: &quot;https://schema.org&quot;,
      &quot;@type&quot;: &quot;BlogPosting&quot;,
      headline: post.title,
      description: post.description,
      datePublished: post.publishedAt,
      author: { &quot;@type&quot;: &quot;Person&quot;, name: &quot;Your Name&quot; },
    }),
  }}
/&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-8-style-blog-content-with-tailwind-typography&quot;&gt;Step 8: Style Blog Content with Tailwind Typography&lt;/h3&gt;
&lt;p&gt;Also unchanged — MDX content renders as plain, unstyled HTML by default regardless of which remote-MDX package compiled it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install @tailwindcss/typography
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;/* app/globals.css */
@import &quot;tailwindcss&quot;;

@plugin &quot;@tailwindcss/typography&quot;;

:root {
  --sh-class: #7aa2f7;
  --sh-sign: #89ddff;
  --sh-string: #9ece6a;
  --sh-keyword: #bb9af7;
  --sh-comment: #acacac;
  --sh-jsxliterals: #7aa2f7;
  --sh-property: #73daca;
  --sh-entity: #e0af68;
  --sh-identifier: #fff;
}

@layer base {
  code:not(pre code) {
    background-color: var(--color-gray-100);
  }

  code:not(pre code) span {
    font-weight: 600;
    color: black !important;
  }

  table {
    display: block;
    max-width: fit-content;
    overflow-x: auto;
    white-space: nowrap;
    text-align: left;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;migrating-an-existing-next-mdx-remote-setup-checklist&quot;&gt;Migrating an Existing next-mdx-remote Setup: Checklist&lt;/h3&gt;
&lt;p&gt;If you already have the original guide's setup running in production, here's the exact diff:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;npm uninstall next-mdx-remote&lt;/code&gt; and &lt;code&gt;npm install next-mdx-remote-client@^2&lt;/code&gt; (or &lt;code&gt;@^1&lt;/code&gt; on React 18)&lt;/li&gt;
&lt;li&gt;[ ] Remove &lt;code&gt;transpilePackages: ['next-mdx-remote']&lt;/code&gt; from &lt;code&gt;next.config.mjs&lt;/code&gt; if present — it's not needed for &lt;code&gt;next-mdx-remote-client&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;[ ] Change &lt;code&gt;import { MDXRemote } from &quot;next-mdx-remote/rsc&quot;&lt;/code&gt; to &lt;code&gt;import { MDXRemote } from &quot;next-mdx-remote-client/rsc&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;[ ] Change &lt;code&gt;options={{ mdxOptions: { remarkPlugins: [remarkGfm] } }}&lt;/code&gt; to a typed &lt;code&gt;MDXRemoteOptions&lt;/code&gt; object (optional, but gets you autocomplete)&lt;/li&gt;
&lt;li&gt;[ ] Add an &lt;code&gt;onError&lt;/code&gt; component and pass it as a prop — you're no longer required to hand-roll this yourself&lt;/li&gt;
&lt;li&gt;[ ] Confirm your route already wraps &lt;code&gt;&amp;lt;MDXRemote /&amp;gt;&lt;/code&gt; in &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; — &lt;code&gt;next-mdx-remote&lt;/code&gt;'s RSC component was also async, so this requirement doesn't actually change&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;mdx-components.tsx&lt;/code&gt;, &lt;code&gt;lib/posts.ts&lt;/code&gt;, your &lt;code&gt;.mdx&lt;/code&gt; content files, and your Tailwind Typography setup all need &lt;strong&gt;zero changes&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For most blogs, this is a genuinely small migration — the package was designed as a drop-in-shaped replacement, not a rewrite.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;frequently-asked-questions&quot;&gt;Frequently Asked Questions&lt;/h3&gt;
&lt;h4 id=&quot;is-next-mdx-remote-client-a-fork-or-a-completely-separate-project-&quot;&gt;Is next-mdx-remote-client a fork or a completely separate project?&lt;/h4&gt;
&lt;p&gt;It's a from-scratch package built independently in 2024, in the spirit of &lt;code&gt;@mdx-js/mdx&lt;/code&gt;, rather than a literal code fork — but it's positioned and documented as a direct alternative to &lt;code&gt;next-mdx-remote&lt;/code&gt;, with a published &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ipikuka/next-mdx-remote-client/blob/main/migration_guide.md&quot;&gt;migration guide&lt;/a&gt; for exactly this switch.&lt;/p&gt;
&lt;h4 id=&quot;do-i-need-to-change-my-mdx-content-files-at-all-&quot;&gt;Do I need to change my .mdx content files at all?&lt;/h4&gt;
&lt;p&gt;No. &lt;code&gt;next-mdx-remote-client&lt;/code&gt; compiles the same MDX syntax, reads the same frontmatter format, and works with the same custom component map. The migration is entirely in your route and import statements, not your content.&lt;/p&gt;
&lt;h4 id=&quot;can-i-still-use-next-mdx-remote-utils-style-frontmatter-parsing-without-gray-matter-&quot;&gt;Can I still use next-mdx-remote/utils-style frontmatter parsing without gray-matter?&lt;/h4&gt;
&lt;p&gt;Yes — use &lt;code&gt;getFrontmatter&lt;/code&gt; from &lt;code&gt;next-mdx-remote-client/utils&lt;/code&gt;, shown in &lt;a rel=&quot;nofollow&quot; href=&quot;#step-3-build-the-data-layer&quot;&gt;Step 3&lt;/a&gt;. It's isolated in its own subpath specifically so it costs almost nothing to import, and the maintainer notes it works fine even if you're still on plain &lt;code&gt;next-mdx-remote&lt;/code&gt; elsewhere in your app.&lt;/p&gt;
&lt;h4 id=&quot;what-s-the-difference-between-mdxremote-and-evaluate-&quot;&gt;What's the difference between MDXRemote and evaluate?&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;MDXRemote&lt;/code&gt; is the simpler option when you only need frontmatter and scope data &lt;em&gt;inside&lt;/em&gt; the MDX body itself. &lt;code&gt;evaluate&lt;/code&gt; is lower-level and returns &lt;code&gt;frontmatter&lt;/code&gt;, &lt;code&gt;scope&lt;/code&gt;, and exported &lt;code&gt;mod&lt;/code&gt; data directly to your page's JSX — reach for it when a byline, reading-time badge, or table-of-contents component outside the MDX needs that data. See &lt;a rel=&quot;nofollow&quot; href=&quot;#step-6-want-frontmatter-and-scope-inside-jsx-use-evaluate-instead&quot;&gt;Step 6&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;page-router-faq&quot;&gt;Does this work with the Pages Router too?&lt;/h3&gt;
&lt;p&gt;Yes, but through a completely separate, isolated API — &lt;code&gt;serialize&lt;/code&gt; (server-side, in &lt;code&gt;getStaticProps&lt;/code&gt;), plus &lt;code&gt;hydrate&lt;/code&gt; and &lt;code&gt;MDXClient&lt;/code&gt; (client-side) — imported from &lt;code&gt;next-mdx-remote-client/serialize&lt;/code&gt; and &lt;code&gt;next-mdx-remote-client/csr&lt;/code&gt; rather than &lt;code&gt;/rsc&lt;/code&gt;. The App Router and Pages Router code paths don't share internals.&lt;/p&gt;
&lt;h4 id=&quot;is-next-mdx-remote-actually-broken-right-now-&quot;&gt;Is next-mdx-remote actually broken right now?&lt;/h4&gt;
&lt;p&gt;No — HashiCorp archived the repository, meaning it's read-only with no further commits, but the last published version (&lt;code&gt;6.0.0&lt;/code&gt;) still installs and runs normally today. Nothing forces an immediate migration. The reason to move is that any future Next.js, React, or &lt;code&gt;@mdx-js/mdx&lt;/code&gt; change that breaks compatibility will never get a fix from the original package.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h3&gt;
&lt;p&gt;The move from &lt;code&gt;next-mdx-remote&lt;/code&gt; to &lt;code&gt;next-mdx-remote-client&lt;/code&gt; is a narrow, mechanical change for most blogs: swap the package, swap one import path, add an &lt;code&gt;onError&lt;/code&gt; handler, and everything else — your content folder, your data layer, your custom components, your Tailwind Typography setup — keeps working exactly as it did. What you get in exchange is a package that's still receiving updates, plus a real error boundary for MDX rendering that the original never shipped.&lt;/p&gt;
&lt;p&gt;If you're starting a brand-new Next.js 16 blog today rather than migrating one, just start here directly — there's no reason to build on the archived package first.&lt;/p&gt;
&lt;h3 id=&quot;useful-resources&quot;&gt;Useful Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ipikuka/next-mdx-remote-client&quot;&gt;next-mdx-remote-client GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ipikuka/next-mdx-remote-client/blob/main/migration_guide.md&quot;&gt;next-mdx-remote-client Migration Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/remarkjs/remark-gfm&quot;&gt;remark-gfm Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/tailwindlabs/tailwindcss-typography&quot;&gt;Tailwind Typography Plugin Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;continue-learning&quot;&gt;Continue Learning&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/mdx-remote-nextjs-16-guide&quot;&gt;Complete MDX Blog Setup with next-mdx-remote and Next.js 16 (original guide)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/nextjs-16-routing-guide&quot;&gt;Next.js 16 Routing Explained: A Complete Beginner's Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/server-vs-client-components-nextjs-16&quot;&gt;Server vs Client Components in Next.js 16&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/technical-seo-checklist&quot;&gt;The Complete Technical SEO Checklist for Next.js Developers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;free-developer-tools&quot;&gt;Free Developer Tools&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/tools/meta-tag-generator&quot;&gt;Meta Tag Generator for Next.js &amp;amp; HTML&lt;/a&gt; — generate SEO meta tags, Open Graph tags, Twitter Cards, and JSON-LD instantly for each post.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/tools/json-ld-generator&quot;&gt;JSON-LD Generator for Next.js &amp;amp; HTML&lt;/a&gt; — generate BlogPosting structured data checked live against Google's Rich Results requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;📦 &lt;strong&gt;Source Code:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Muhammad-Ali-sma/next-mdx-remote-client-nextjs-16-setup&quot;&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25579/next-mdx-remote-client-guide-for-next-js-16-2026</guid>
<pubDate>Fri, 28 Aug 2026 03:40:16 +0000</pubDate>
</item>
</channel>
</rss>