next-mdx-remote

Load, compile, and hydrate MDX content from any remote source—CMS, database, or API—inside Next.js apps, without a build-time file import.

Library
npm
v6.0.0
3,067 stars
Mozilla Public License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
50 /100 Fair
Development Activity 4
Maintenance 44
Community 52
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
70 /100 Good
Architecture 75
Code Quality 80
Innovation 70
Learning Curve 55

next-mdx-remote is a small utility library that lets Next.js applications render MDX content that doesn’t live as a local file at build time. A serialize() function runs server-side (inside getStaticProps, getServerSideProps, or a Server Component) to compile a raw MDX string—pulled from a CMS, database, or any remote source—into a portable, JSON-safe compiledSource payload plus parsed frontmatter and scope. That payload is then handed to the client-side <MDXRemote /> component (or, for the React Server Components path, the single-call compileMDX()/MDXRemote async function), which evaluates the compiled source at runtime and renders it through @mdx-js/react’s MDXProvider, with consumer-supplied components available inside the MDX.

The library also adds guardrails aimed at the specific risk of compiling MDX from untrusted or remote sources: import/export statements are stripped by default, and JavaScript expressions ({variable}, {func()}) are blocked unless explicitly allowed, with a best-effort filter for dangerous globals like eval and process when they are. As of early 2026 the project has been archived by its maintainers and is no longer actively supported, so it should be evaluated as a stable, no-further-development dependency rather than an actively maintained one.

What You Get

  • A serialize() function that compiles a raw MDX string—pulled from anywhere—into a portable compiledSource object usable inside getStaticProps/getServerSideProps.
  • A <MDXRemote /> client component that hydrates compiled MDX with your own React components mapped in via a components prop.
  • A dedicated next-mdx-remote/rsc entry point exposing compileMDX() and an async MDXRemote for React Server Components, collapsing compile+render into a single call.
  • Optional frontmatter parsing (parseFrontmatter: true), a scope object for passing template variables into MDX, and a lazy hydration mode that defers client-side hydration via requestIdleCallback.
  • Built-in safety controls (blockJS, blockDangerousJS) for compiling MDX from sources you don’t fully trust.

Common Use Cases

  • Rendering blog posts or docs stored in a headless CMS or database instead of as local MDX files.
  • Building a marketing site where non-technical editors write MDX content that’s fetched at request/build time from an external API.
  • Server Components apps that want to compile and render remote MDX in one async call via compileMDX.
  • Multi-tenant or user-generated-content platforms that need to safely compile MDX-like text without exposing arbitrary JS execution.

Under The Hood

Architecture

The package exposes three entry points via package.json exports: the root (src/index.tsx) for client-side hydration, next-mdx-remote/rsc (src/rsc.tsx) for React Server Components, and next-mdx-remote/serialize (src/serialize.ts) for the server-side compile step—a clean split between compiling MDX and rendering it. serialize() calls @mdx-js/mdx’s compile() with a chain of remark plugins assembled in getCompileOptions() (stripping imports/exports, blocking JS expressions or dangerous globals), producing a self-contained function-body compiledSource string plus frontmatter and scope. On the client, MDXRemote evaluates that string via Reflect.construct(Function, ...), closing over React, the MDX runtime, and consumer-supplied components/scope, then renders through MDXProvider; the RSC path’s compileMDX does the same construct-and-eval trick synchronously inside an async server function, skipping the serialize-to-props round trip entirely. This eval bridge is the one abstraction the whole library depends on—change how @mdx-js/mdx’s output format shapes compiledSource and every consumer breaks.

Tech Stack

TypeScript compiled via tsc to flat index.js/serialize.js/rsc.js entry files (no bundler), built on @mdx-js/mdx and @mdx-js/react ^3 for MDX compilation and context, vfile/vfile-matter for the compiled-file model and frontmatter parsing, unist-util-visit/unist-util-remove for the custom remark plugins, and @babel/code-frame for formatted compiler error output. The only peer dependency is react >=16. Tooling includes Changesets for versioning and canary/stable release workflows in GitHub Actions, Vitest for tests, tsc --noEmit for type-checking, and Prettier with a husky pre-commit hook; the test harness spins up a real Next.js app plus Puppeteer for browser-level integration tests.

Code Quality

Tests live in __tests__/ (integration, RSC, and serialize suites plus a fixtures Next.js app) run via Vitest, with a pretest step that builds and npm-packs the library first so tests exercise the actual published output rather than raw source—a stronger-than-typical setup. Type safety is thorough: a fully generic public API (MDXRemoteSerializeResult<TScope, TFrontmatter>), a dedicated test:types script, and hand-authored .d.ts files alongside generated ones. Error handling is explicit and user-facing—createFormattedMDXError parses the MDX compiler’s error message for line/column info and re-throws a formatted error with a code frame instead of letting a raw compiler exception surface. Naming is consistent across the three entry points, Prettier plus a pre-commit hook enforce formatting, and CI runs the test suite on every PR; the one gap is no linter configured beyond Prettier.

API Design The public surface is small and its names mirror each other across entry points (serialize / MDXRemote / compileMDX), so getting started requires little boilerplate—import serialize, call it server-side, pass the result to <MDXRemote />. The two-step server/client split (compile, then separately render) does ask consumers to understand and thread scope/components between two call sites, which is the main source of friction; the newer RSC compileMDX collapses this into a single async call and is meaningfully more ergonomic. Documentation lives entirely in an extensive README with collapsible sections covering common patterns (frontmatter, scope, MDXProvider, lazy hydration)—thorough, but there’s no separate API reference or docs site.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers