All examples have been taken from next-mdx-remote
to show exactly how to migrate the code.
Main example in the github page headline
- import { serialize } from 'next-mdx-remote/serialize'
- import { MDXRemote } from 'next-mdx-remote'
+ import { serialize } from 'next-mdx-remote-client/serialize'
+ import { MDXClient } from 'next-mdx-remote-client'
import { Test } from '../mdxComponents'
+ import { ErrorComponent } from '../components'
const components = { Test }
export default function TestPage({ source }) {
+ if ("error" in source) {
+ return <ErrorComponent error={source.error} />;
+ }
return (
<div className="wrapper">
- <MDXRemote {...source} components={components} />
+ <MDXClient {...source} components={components} />
</div>
)
}
export async function getStaticProps() {
// MDX text - can be from a local file, database, anywhere
const source = 'Some **mdx** text, with a component <Test />'
- const mdxSource = await serialize(source)
+ const mdxSource = await serialize({source})
return { props: { source: mdxSource } }
}
Parsing frontmatter
- import { serialize } from 'next-mdx-remote/serialize'
- import { MDXRemote } from 'next-mdx-remote'
+ import { serialize } from 'next-mdx-remote-client/serialize'
+ import { MDXClient } from 'next-mdx-remote-client'
import { Test } from '../mdxComponents'
+ import { ErrorComponent } from '../components'
const components = { Test }
export default function TestPage({ mdxSource }) {
+ if ("error" in mdxSource) {
+ return <ErrorComponent error={mdxSource.error} />;
+ }
return (
<div className="wrapper">
<h1>{mdxSource.frontmatter.title}</h1>
- <MDXRemote {...mdxSource} components={components} />
+ <MDXClient {...mdxSource} components={components} />
</div>
)
}
export async function getStaticProps() {
// MDX text - can be from a local file, database, anywhere
const source = `---
title: Test
---
Some **mdx** text, with a component <Test name={frontmatter.title}/>`
- const mdxSource = await serialize(source, { parseFrontmatter: true })
+ const mdxSource = await serialize({source, options: { parseFrontmatter: true }})
return { props: { mdxSource } }
}
Passing custom data to a component with `scope`
- import { serialize } from 'next-mdx-remote/serialize'
- import { MDXRemote } from 'next-mdx-remote'
+ import { serialize } from 'next-mdx-remote-client/serialize'
+ import { MDXClient } from 'next-mdx-remote-client'
import { Test } from '../mdxComponents'
+ import { ErrorComponent } from '../components'
const components = { Test }
const data = { product: 'next' }
export default function TestPage({ source }) {
+ if ("error" in source) {
+ return <ErrorComponent error={source.error} />;
+ }
return (
<div className="wrapper">
- <MDXRemote {...source} components={components} scope={data} />
+ <MDXClient {...source} components={components} scope={data} />
</div>
)
}
export async function getStaticProps() {
// MDX text - can be from a local file, database, anywhere
const source =
'Some **mdx** text, with a component using a scope variable <Test product={product} />'
- const mdxSource = await serialize(source)
+ const mdxSource = await serialize({source})
return { props: { source: mdxSource } }
}
Passing `scope` into the `serialize` function instead
- import { serialize } from 'next-mdx-remote/serialize'
- import { MDXRemote } from 'next-mdx-remote'
+ import { serialize } from 'next-mdx-remote-client/serialize'
+ import { MDXClient } from 'next-mdx-remote-client'
import { Test } from '../mdxComponents'
+ import { ErrorComponent } from '../components'
const components = { Test }
const data = { product: 'next' }
export default function TestPage({ source }) {
+ if ("error" in source) {
+ return <ErrorComponent error={source.error} />;
+ }
return (
<div className="wrapper">
- <MDXRemote {...source} components={components} />
+ <MDXClient {...source} components={components} />
</div>
)
}
export async function getStaticProps() {
// MDX text - can be from a local file, database, anywhere
const source =
'Some **mdx** text, with a component <Test product={product} />'
- const mdxSource = await serialize(source, { scope: data })
+ const mdxSource = await serialize({source, options: { scope: data }})
return { props: { source: mdxSource } }
}
Custom components from MDXProvider
// pages/_app.jsx
- import { MDXProvider } from '@mdx-js/react'
+ import { MDXProvider } from 'next-mdx-remote-client'
import Test from '../mdxComponents/Test'
const components = { Test }
export default function MyApp({ Component, pageProps }) {
return (
<MDXProvider components={components}>
<Component {...pageProps} />
</MDXProvider>
)
}
// pages/test.jsx
- import { serialize } from 'next-mdx-remote/serialize'
- import { MDXRemote } from 'next-mdx-remote'
+ import { serialize } from 'next-mdx-remote-client/serialize'
+ import { MDXClient } from 'next-mdx-remote-client'
+ import { ErrorComponent } from '../components'
export default function TestPage({ source }) {
+ if ("error" in source) {
+ return <ErrorComponent error={source.error} />;
+ }
return (
<div className="wrapper">
- <MDXRemote {...source} />
+ <MDXClient {...source} />
</div>
)
}
export async function getStaticProps() {
// MDX text - can be from a local file, database, anywhere
const source = 'Some **mdx** text, with a component <Test />'
- const mdxSource = await serialize(source)
+ const mdxSource = await serialize({source})
return { props: { source: mdxSource } }
}
Component names with dot (e.g. motion.div
)
import { motion } from 'framer-motion'
- import { MDXProvider } from '@mdx-js/react'
- import { serialize } from 'next-mdx-remote/serialize'
- import { MDXRemote } from 'next-mdx-remote'
+ import { serialize } from 'next-mdx-remote-client/serialize'
+ import { MDXClient, MDXProvider } from 'next-mdx-remote-client'
+ import { ErrorComponent } from '../components'
export default function TestPage({ source }) {
+ if ("error" in source) {
+ return <ErrorComponent error={source.error} />;
+ }
return (
<div className="wrapper">
- <MDXRemote {...source} components={{ motion }} />
+ <MDXClient {...source} components={{ motion }} />
</div>
)
}
export async function getStaticProps() {
// MDX text - can be from a local file, database, anywhere
const source = `Some **mdx** text, with a component:
<motion.div animate={{ x: 100 }} />`
- const mdxSource = await serialize(source)
+ const mdxSource = await serialize({source})
return { props: { source: mdxSource } }
}
Lazy hydration
- import { serialize } from 'next-mdx-remote/serialize'
- import { MDXRemote } from 'next-mdx-remote'
+ import { serialize } from 'next-mdx-remote-client/serialize'
+ import { MDXClientLazy } from 'next-mdx-remote-client'
import { Test } from '../mdxComponents'
+ import { ErrorComponent } from '../components'
const components = { Test }
export default function TestPage({ source }) {
+ if ("error" in source) {
+ return <ErrorComponent error={source.error} />;
+ }
return (
<div className="wrapper">
- <MDXRemote {...source} components={components} lazy />
+ <MDXClientLazy {...source} components={components} />
</div>
)
}
export async function getStaticProps() {
// MDX text - can be from a local file, database, anywhere
const source = 'Some **mdx** text, with a component <Test />'
- const mdxSource = await serialize(source)
+ const mdxSource = await serialize({source})
return { props: { source: mdxSource } }
}
With typescript
import { type GetStaticProps } from 'next'
- import { serialize } from 'next-mdx-remote/serialize'
- import { MDXRemote, type MDXRemoteSerializeResult } from 'next-mdx-remote'
+ import { serialize, type SerializeResult } from 'next-mdx-remote-client/serialize'
+ import { MDXClient } from 'next-mdx-remote-client'
import { ExampleComponent } from './mdxComponents'
+ import { ErrorComponent } from '../components'
const components = { ExampleComponent }
- interface Props {
- mdxSource: MDXRemoteSerializeResult
- }
+ type Props = {
+ mdxSource: SerializeResult
+ }
export default function ExamplePage({ mdxSource }: Props) {
+ if ("error" in mdxSource) {
+ return <ErrorComponent error={mdxSource.error} />;
+ }
return (
<div>
+ <MDXRemote {...mdxSource} components={components} />
- <MDXClient {...mdxSource} components={components} />
</div>
)
}
export const getStaticProps: GetStaticProps<{
- mdxSource: MDXRemoteSerializeResult
+ mdxSource: SerializeResult
}> = async () => {
- const mdxSource = await serialize('some *mdx* content: <ExampleComponent />')
+ const mdxSource = await serialize({ source: 'some *mdx* content: <ExampleComponent />'})
return { props: { mdxSource } }
}
All examples have been taken from next-mdx-remote
to show exactly how to migrate the code.
Basic example for React Server Components (RSC)
- import { MDXRemote } from 'next-mdx-remote/rsc'
+ import { MDXRemote } from 'next-mdx-remote-client/rsc'
+ import { ErrorComponent } from '../components'
// app/page.js
export default function Home() {
return (
<MDXRemote
source={`# Hello from Server Components`}
+ onEror={ErrorComponent}
/>
)
}
Loading state in "app" router
import { Suspense } from 'react'
- import { MDXRemote } from 'next-mdx-remote/rsc'
+ import { MDXRemote } from 'next-mdx-remote-client/rsc'
+ import { ErrorComponent } from '../components'
// app/page.js
export default function Home() {
return (
// In Next.js you can also use `loading.js` instead of <Suspense />
<Suspense fallback={<>Loading...</>}>
<MDXRemote
source={`# Hello from Server Components`}
+ onEror={ErrorComponent}
/>
</Suspense>
)
}
Custom components
// components/mdx-remote.js
- import { MDXRemote } from 'next-mdx-remote/rsc'
+ import { MDXRemote } from 'next-mdx-remote-client/rsc'
+ import { ErrorComponent } from '../components'
const components = {
h1: (props) => (
<h1 {...props} className="large-text">
{props.children}
</h1>
),
}
export function CustomMDX(props) {
return (
<MDXRemote
{...props}
components={{ ...components, ...(props.components || {}) }}
+ onEror={ErrorComponent}
/>
)
}
// app/page.js
import { CustomMDX } from '../components/mdx-remote'
export default function Home() {
return (
<CustomMDX
// h1 now renders with `large-text` className
source={`# Hello from Server Components`}
/>
)
}
Access `frontmatter` and `scope` outside of MDX
// app/page.js
- import { compileMDX } from 'next-mdx-remote/rsc'
+ import { evaluate } from 'next-mdx-remote-client/rsc'
+ import { ErrorComponent } from '../components'
export default async function Home() {
- const { content, frontmatter } = await compileMDX<{ title: string }>({
+ const { content, frontmatter, scope, error } = await evaluate<{ title: string }>({
source: `---
title: RSC Frontmatter Example
---
# Hello from {product}!`,
options: {
parseFrontmatter: true,
scope: {
product: 'Server Components'
},
},
})
+ if (error) {
+ return <ErrorComponent error={error} />;
+ }
return (
<>
<h1>{frontmatter.title}</h1>
+ <h2>{scope.product}</h2>
{content}
</>
)
}
🟥 next-mdx-remote
:
import /* */ from "next-mdx-remote";
import /* */ from "next-mdx-remote/serialize";
import /* */ from "next-mdx-remote/rsc";
🟩 next-mdx-remote-client
:
import /* */ from "next-mdx-remote-client";
import /* */ from "next-mdx-remote-client/serialize";
import /* */ from "next-mdx-remote-client/rsc";
// additional exported subpaths
import /* */ from "next-mdx-remote-client/csr";
import /* */ from "next-mdx-remote-client/utils";
Note
The next-mdx-remote-client
and the next-mdx-remote-client/csr
refer to the same.
🟥 next-mdx-remote
:
It exports the components with the same name <MDXRemote />
for both "app" and "pages" router.
// for "pages" router, it is a client component
import { MDXRemote } from "next-mdx-remote";
// for "app" router, it is a server component
import { MDXRemote } from "next-mdx-remote/rsc";
🟩 next-mdx-remote-client
:
It exports <MDXClient />
for "pages" router, <MDXRemote />
for "app" router.
// for "pages" router, it is a client component
import { MDXClient } from "next-mdx-remote-client/csr";
// for "app" router, it is a server component
import { MDXRemote } from "next-mdx-remote-client/rsc";
Go to the part associated with Next.js pages router
🟥 next-mdx-remote
:
import { MDXRemote, compileMDX } from "next-mdx-remote/rsc";
🟩 next-mdx-remote-client
:
import { MDXRemote, evaluate } from "next-mdx-remote-client/rsc";
🟥 next-mdx-remote
:
import type { MDXRemoteProps, CompileMDXResult, MDXRemoteSerializeResult } from "next-mdx-remote/rsc";
🟩 next-mdx-remote-client
:
import type { MDXRemoteProps, MDXRemoteOptions } from "next-mdx-remote-client/rsc";
import type { EvaluateProps, EvaluateOptions, EvaluateResult } from "next-mdx-remote-client/rsc";
🟥 next-mdx-remote
:
The compileMDX
and MDXRemote
take the same props MDXRemoteProps
.
type MDXRemoteProps = {
source: VFileCompatible
options?: SerializeOptions
components?: React.ComponentProps<typeof MDXProvider>['components']
}
🟩 next-mdx-remote-client
:
The evaluate
takes EvaluateProps
which has a type argument, and the same prop keys but different types.
type EvaluateProps<TScope> = {
source: Compatible;
options?: EvaluateOptions<TScope>;
components?: MDXComponents;
}
The MDXRemote
takes MDXRemoteProps
which has onError
prop in addition to EvaluateProps
.
type MDXRemoteProps<TScope> = {
source: Compatible;
options?: EvaluateOptions<TScope>;
components?: MDXComponents;
onError?: React.ComponentType<{ error: Error }>;
}
🟥 next-mdx-remote
:
The compileMDX
takes MDXRemoteProps
and returns CompileMDXResult
as a promise.
async function compileMDX<Frontmatter>({source, options, components}): Promise<CompileMDXResult> {}
The compileMDX
takes one generic type parameter <Frontmatter>
.
🟩 next-mdx-remote-client
:
The evaluate
takes EvaluateProps
and returns EvaluateResult
as a promise.
async function evaluate<Frontmatter, Scope>({source, options, components}): Promise<EvaluateResult> {}
The evaluate
takes two generic type parameters <Frontmatter, Scope>
(the order matters).
🟥 next-mdx-remote
:
The compileMDX
returns the MDX content
and the frontmatter
.
const { content, frontmatter } = await compileMDX<Frontmatter>({source, options, components});
type CompileMDXResult<TFrontmatter> = {
content: React.ReactElement
frontmatter: TFrontmatter
}
🟩 next-mdx-remote-client
:
The evaluate
returns mod
object for the exports, scope
, and error
objects additional to MDX content
and the frontmatter
.
const { content, mod, frontmatter, scope, error } = await evaluate<Frontmatter, Scope>({source, options, components});
type EvaluateResult<TFrontmatter, TScope> = {
content: JSX.Element;
mod: Record<string, unknown>;
frontmatter: TFrontmatter;
scope: TScope;
error?: Error;
}
The next-mdx-remote-client
has more options.
Please, note that, the mdxOptions
are more or less similar in both but have differences in terms of opinionation for @mdx-js/mdx
options.
🟥 next-mdx-remote
:
The compileMDX
and MDXRemote
options:
{
mdxOptions?: /* mdx options to be passed into @mdx-js/mdx */
parseFrontmatter?: boolean
scope?: Record<string, unknown>
}
🟩 next-mdx-remote-client
:
The evaluate
and MDXRemote
options:
{
mdxOptions?: /* mdx options to be passed into @mdx-js/mdx */
disableExports?: boolean; // <---
disableImports?: boolean; // <---
parseFrontmatter?: boolean;
scope?: Record<string, unknown>;
vfileDataIntoScope?: VfileDataIntoScope; // <---
}
Go to the part associated with Next.js app router
🟥 next-mdx-remote
:
const mdxSource = await serialize(source: VFileCompatible, options: SerializeOptions, rsc: boolean);
🟩 next-mdx-remote-client
:
const mdxSource = await serialize({ source: Compatible, options: SerializeOptions });
🟥 next-mdx-remote
:
type SerializeOptions = {
mdxOptions?: SerializeMdxOptions;
parseFrontmatter?: boolean;
scope?: Record<string, unknown>;
};
🟩 next-mdx-remote-client
:
type SerializeOptions<TScope> = {
mdxOptions?: SerializeMdxOptions;
disableExports?: boolean; // <---
disableImports?: boolean; // <---
parseFrontmatter?: boolean;
scope?: TScope;
vfileDataIntoScope?: VfileDataIntoScope; // <---
};
🟥 next-mdx-remote
:
const mdxSource = await serialize<Scope, Frontmatter>(/* */);
🟩 next-mdx-remote-client
:
const mdxSource = await serialize<Frontmatter, Scope>(/* */);
🟥 next-mdx-remote
:
type Props = {
mdxSource: MDXRemoteSerializeResult<Scope, Frontmatter>;
};
🟩 next-mdx-remote-client
:
type Props = {
mdxSource: SerializeResult<Frontmatter, Scope>;
};
🟥 next-mdx-remote
:
It has no "error" object in return.
const { compiledSource, frontmatter, scope } = await serialize(/* */);
🟩 next-mdx-remote-client
:
const { compiledSource, frontmatter, scope, error } = await serialize(/* */);
🟥 next-mdx-remote
:
It doesn't have the function
🟩 next-mdx-remote-client
:
const { content, mod, error } = hydrate(props: HydrateProps): HydrateResult {}
type HydrateProps = {
compiledSource: string;
frontmatter?: Record<string, unknown>;
scope?: Record<string, unknown>;
components?: MDXComponents;
disableParentContext?: boolean;
};
type HydrateResult = {
content: JSX.Element;
mod: Record<string, unknown>;
error?: Error;
};
🟥 next-mdx-remote
:
<MDXRemote
compiledSource={compiledSource}
frontmatter={frontmatter}
scope={scope}
components={components}
lazy={lazy} // boolean, optional
/>
🟩 next-mdx-remote-client
:
<MDXClient
compiledSource={compiledSource}
frontmatter={frontmatter}
scope={scope}
components={components}
onError={onError} // React.ComponentType<{ error: Error }>, optional
/>
🟥 next-mdx-remote
:
<MDXRemote
compiledSource={compiledSource}
// ..
lazy
/>
🟩 next-mdx-remote-client
:
const { content, mod, error } = hydrateLazy({compiledSource, /* */ })
or
<MDXClientLazy
compiledSource={compiledSource}
// ..
/>