Why I picked next-mdx-remote/rsc over @next/mdx
Notes on choosing an MDX engine for a Next.js 16 + next-intl blog with locale-aware routing — and why the file-system option was the wrong fit.
When I started building this blog at /[locale]/blog/... inside an existing
Next.js 16 + next-intl site, I had three real options for MDX: @next/mdx,
next-mdx-remote/rsc, or rolling my own with react-markdown and a
frontmatter parser. The constraint that ruled the day was locale routing.
The collision
@next/mdx lets MDX files act as Next.js pages — drop a page.mdx into
src/app/foo/ and it becomes /foo. That's elegant when MDX is your
routing primitive. It is not elegant when your route already has a dynamic
segment in front of it.
For me, every page lives under [locale]. To make @next/mdx work I'd have
to either:
- duplicate every post per locale folder (defeating the point), or
- implement a shimmed
generateStaticParamsthat maps file-system MDX into the locale segment — possible, fragile, and at that point you've reinvented the loader anyway.
Why content-as-data wins
The actual content model I want is frontmatter-driven. A post is data
with a body. It has a lang, a date, tags. The locale routing should query
that data and decide where the post lives, not the other way around.
const PostFrontmatter = z.object({
title: z.string(),
description: z.string(),
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
tags: z.array(z.string()).min(1),
lang: z.enum(routing.locales),
});
With next-mdx-remote/rsc, the loader walks /content/blog/, validates
frontmatter against this schema, and returns posts. The [slug] route asks
"is there a post with this slug whose lang matches the active locale?" If
no, 404. If yes, render via <MDXRemote>. Server-only, zero MDX runtime in
the client bundle.
What I gave up
Compile-time errors point to MDX syntax issues by file path but with less
inline context than @next/mdx (which fails in your IDE). Worth it.
When @next/mdx would be right
A docs site where MDX is the canonical content type and routing is a 1:1 reflection of the file system. That's not a blog with locales.
Related