From 0a168b1ff7122f43d03146a42230839b7ad13c1b Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Fri, 20 Jan 2023 11:46:55 -0600 Subject: [PATCH] chore: document new RSC API (#333) --- README.md | 146 ++++++++++++++++++++++++++++++++++++++++++++++---- src/index.tsx | 9 +++- src/rsc.tsx | 5 +- 3 files changed, 146 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 410e43e..9e8ef98 100644 --- a/README.md +++ b/README.md @@ -305,7 +305,7 @@ This library exposes a function and a component, `serialize` and `` mdxOptions: { remarkPlugins: [], rehypePlugins: [], - format: 'mdx' + format: 'mdx', }, // Indicates whether or not to parse the frontmatter from the mdx source parseFrontmatter: false, @@ -407,18 +407,144 @@ export default function ExamplePage({ mdxSource }: Props) { ) } -export const getStaticProps: GetStaticProps<{mdxSource: MDXRemoteSerializeResult}> = - async () => { - const mdxSource = await serialize( - 'some *mdx* content: ' - ) - return { props: { mdxSource } } - } +export const getStaticProps: GetStaticProps<{ + mdxSource: MDXRemoteSerializeResult +}> = async () => { + const mdxSource = await serialize('some *mdx* content: ') + return { props: { mdxSource } } +} +``` + +## React Server Components (RSC) & Next.js `app` Directory Support + +> **Warning** +> Server Components and Next.js's `app` directory are unstable, and so we consider the `next-mdx-remote/rsc` API to be unstable as well. Use at your own discretion, and be aware that the API and behavior might change between minor and/or patch releases. + +Usage of `next-mdx-remote` within server components, and specifically within Next.js's `app` directory (beta), is supported by importing from `next-mdx-remote/rsc`. Previously, the serialization and render steps were separate, but going forward RSC makes this separation unnecessary. + +Some noteworthy differences: + +- `` now accepts a `source` prop, instead of accepting the serialized output from `next-mdx-remote/serialize` +- Custom components can no longer be provided by using the `MDXProvider` context from `@mdx-js/react`, as RSC does not support React Context +- To access frontmatter outside of your MDX when passing `parseFrontmatter: true`, use the `compileMdx` method exposed from `next-mdx-remote/rsc` +- The `lazy` prop is no longer supported, as the rendering happens on the server +- `` must be rendered on the server, as it is now an async component. Client components can be rendered as part of the MDX markup + +For more information on RSC, check out the [Next.js beta documentation](https://beta.nextjs.org/docs/rendering/server-and-client-components#server-components). + +### Examples + +_Assuming usage in a Next.js 13+ application using the `app` directory._ + +#### Basic + +```tsx +import { MDXRemote } from 'next-mdx-remote/rsc' + +// app/page.js +export default function Home() { + return ( + + ) +} +``` + +#### Loading state + +```tsx +import { MDXRemote } from 'next-mdx-remote/rsc' + +// app/page.js +export default function Home() { + return ( + // Ideally this loading spinner would ensure there is no layout shift, + // this is an example for how to provide such a loading spinner. + // In Next.js you can also use `loading.js` for this. + Loading...}> + + + ) +} +``` + +#### Custom Components + +```tsx +// components/mdx-remote.js +import { MDXRemote } from 'next-mdx-remote/rsc' + +const components = { + h1: (props) => ( +

+ {props.children} +

+ ), +} + +export function CustomMDX(props) { + return ( + + ) +} +``` + +```tsx +// app/page.js +import { CustomMDX } from '../components/mdx-remote' + +export default function Home() { + return ( + + ) +} ``` -## Migrating from v2 to v3 +#### Access Frontmatter outside of MDX -See https://github.com/hashicorp/next-mdx-remote/releases/tag/3.0.0 +```tsx +// app/page.js +import { compileMDX } from "next-mdx-remote/rsc"; + +export default async function Home() { + const {content, frontmatter} = compileMDX({ + source: ` + --- + title: RSC Frontmatter Example + --- + # Hello World + This is from Server Components! + ` + options: { parseFrontmatter: true } + }) + return ( + <> +

{frontmatter.title}

+ {content} + + ); +} +``` ## License diff --git a/src/index.tsx b/src/index.tsx index a0b6351..5e5c728 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -98,8 +98,13 @@ export function MDXRemote({ ) } - // Pass custom markdown components (such as "h1" or "a") via the "components" object - const content = + // wrapping the content with MDXProvider will allow us to customize the standard + // markdown components (such as "h1" or "a") with the "components" object + const content = ( + + + + ) // If lazy = true, we need to render a wrapping div to preserve the same markup structure that was SSR'd return lazy ?
{content}
: content diff --git a/src/rsc.tsx b/src/rsc.tsx index a01976e..87459e0 100644 --- a/src/rsc.tsx +++ b/src/rsc.tsx @@ -2,18 +2,19 @@ import React from 'react' import { jsxRuntime } from './jsx-runtime.cjs' import { MDXRemoteSerializeResult, SerializeOptions } from './types' import { VFileCompatible } from 'vfile' +import { MDXProvider } from '@mdx-js/react' import { serialize } from './serialize' export type MDXRemoteProps = MDXRemoteSerializeResult & { source: VFileCompatible options?: SerializeOptions /** - * A object mapping names to React components. + * An object mapping names to React components. * The key used will be the name accessible to MDX. * * For example: `{ ComponentName: Component }` will be accessible in the MDX as ``. */ - components?: any + components?: React.ComponentProps['components'] } export { MDXRemoteSerializeResult }