Skip to content

Commit

Permalink
Merge branch 'canary' into patch-3
Browse files Browse the repository at this point in the history
  • Loading branch information
miki-tebe authored Dec 18, 2024
2 parents 7b4e654 + a216207 commit 9454e62
Show file tree
Hide file tree
Showing 39 changed files with 442 additions and 333 deletions.
5 changes: 3 additions & 2 deletions examples/with-mdx-remote/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local
# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel
Expand Down
61 changes: 20 additions & 41 deletions examples/with-mdx-remote/README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
# MDX Remote Example

This example shows how a simple blog might be built using the [next-mdx-remote](https://github.com/hashicorp/next-mdx-remote) library, which allows mdx content to be loaded via `getStaticProps` or `getServerSideProps`. The mdx content is loaded from a local folder, but it could be loaded from a database or anywhere else.

The example also showcases [next-remote-watch](https://github.com/hashicorp/next-remote-watch), a library that allows next.js to watch files outside the `pages` folder that are not explicitly imported, which enables the mdx content here to trigger a live reload on change.

Since `next-remote-watch` uses undocumented Next.js APIs, it doesn't replace the default `dev` script for this example. To use it, run `npm run dev:watch` or `yarn dev:watch`.
This example shows how a simple blog might be built using the [next-mdx-remote](https://github.com/hashicorp/next-mdx-remote) library, which allows mdx content to be loaded via [`generateStaticParams`](https://nextjs.org/docs/app/api-reference/functions/generate-static-params). The mdx content is loaded from a local folder, but it could be loaded from a database or anywhere else.

## Deploy your own

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or preview live with [StackBlitz](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-mdx-remote)

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-mdx-remote&project-name=with-mdx-remote&repository-name=with-mdx-remote)

## How to use

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), [pnpm](https://pnpm.io), or [Bun](https://bun.sh/docs/cli/bunx) to bootstrap the example:

```bash
npx create-next-app --example with-mdx-remote with-mdx-remote-app
Expand All @@ -28,56 +24,39 @@ yarn create next-app --example with-mdx-remote with-mdx-remote-app
pnpm create next-app --example with-mdx-remote with-mdx-remote-app
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
```bash
bunx create-next-app --example with-mdx-remote with-mdx-remote-app
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/app/building-your-application/deploying)).

## Notes

### Conditional custom components

When using `next-mdx-remote`, you can pass custom components to the MDX renderer. However, some pages/MDX files might use components that are used infrequently, or only on a single page. To avoid loading those components on every MDX page, you can use `next/dynamic` to conditionally load them.
When using `next-mdx-remote`, you can pass custom components to the MDX renderer. However, some pages/MDX files might use components that are used infrequently, or only on a single page. To avoid loading those components on every MDX page, you can use [`next/dynamic`](https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading#nextdynamic) to conditionally load them.

For example, here's how you can change `getStaticProps` to pass a list of component names, checking the names in the page render function to see which components need to be dynamically loaded.

```js
```typescript
import dynamic from "next/dynamic";
import Test from "../components/test";
import Test from "@/components/test";
import { MDXRemote, type MDXRemoteProps } from 'next-mdx-remote/rsc'

const SomeHeavyComponent = dynamic(() => import("SomeHeavyComponent"));
const SomeHeavyComponent = dynamic(() => import("../component/SomeHeavyComponent"));

const defaultComponents = { Test };

export function SomePage({ mdxSource, componentNames }) {
export function CustomMDX(props: MDXRemoteProps) {
const componentNames = [
/<SomeHeavyComponent/.test(props.source as string) ? "SomeHeavyComponent" : "",
].filter(Boolean);

const components = {
...defaultComponents,
SomeHeavyComponent: componentNames.includes("SomeHeavyComponent")
? SomeHeavyComponent
: null,
: () => null,
};

return <MDXRemote {...mdxSource} components={components} />;
}

export async function getStaticProps() {
const source = `---
title: Conditional custom components
---
Some **mdx** text, with a default component <Test name={title}/> and a Heavy component <SomeHeavyComponent />
`;

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

const componentNames = [
/<SomeHeavyComponent/.test(content) ? "SomeHeavyComponent" : null,
].filter(Boolean);

const mdxSource = await serialize(content);

return {
props: {
mdxSource,
componentNames,
},
};
return <MDXRemote {...props} components={components} />;
}
```
3 changes: 3 additions & 0 deletions examples/with-mdx-remote/app/[slug]/page.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.description {
opacity: 0.6;
}
45 changes: 45 additions & 0 deletions examples/with-mdx-remote/app/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { notFound } from "next/navigation";
import { CustomMDX } from "@/components/mdx";
import { getPosts } from "@/lib/utils";
import styles from "./page.module.css";
import Link from "next/link";

export function generateStaticParams() {
const posts = getPosts();

return posts.map((post) => ({
slug: post.slug,
}));
}

export default async function Blog({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const slug = (await params).slug;
const post = getPosts().find((post) => post.slug === slug);

if (!post) {
notFound();
}

return (
<>
<header>
<nav>
<Link href="/">👈 Go back home</Link>
</nav>
</header>
<main>
<h1 className={styles.postHeader}>{post.metadata.title}</h1>
{post.metadata.description && (
<p className={styles.description}>{post.metadata.description}</p>
)}
<article>
<CustomMDX source={post.content} />
</article>
</main>
</>
);
}
59 changes: 59 additions & 0 deletions examples/with-mdx-remote/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
:root {
--background: #ffffff;
--foreground: #171717;
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}

html,
body {
font: 100%/1.5 system-ui;
max-width: 100vw;
overflow-x: hidden;
}

body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
max-width: 36rem;
margin: 0 auto;
padding: 1.5rem;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

* {
box-sizing: border-box;
padding: 0;
margin: 0;
}

a {
color: inherit;
text-decoration-thickness: 2px;
}

a:hover {
color: royalblue;
text-decoration-color: currentcolor;
}

p {
margin-bottom: 1.5rem;
}

code {
font-family: Menlo;
}

@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}
32 changes: 32 additions & 0 deletions examples/with-mdx-remote/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});

export const metadata: Metadata = {
title: "MDX Remote App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable}`}>
{children}
</body>
</html>
);
}
23 changes: 23 additions & 0 deletions examples/with-mdx-remote/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Link from "next/link";
import { getPosts } from "@/lib/utils";

export default function Home() {
const posts = getPosts();

return (
<main>
<h1>Home Page</h1>
<p>
Click the link below to navigate to a page generated by{" "}
<code>next-mdx-remote</code>.
</p>
<ul>
{posts.map((post) => (
<li key={post.slug}>
<Link href={`/${post.slug}`}>{post.metadata.title}</Link>
</li>
))}
</ul>
</main>
);
}
12 changes: 12 additions & 0 deletions examples/with-mdx-remote/app/posts/example-post.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Example Post
description: This frontmatter description will appear below the title
---

This is an example post, with with a [link](https://nextjs.org) and a React component:

<Greet name="next-mdx-remote" />

Links are rendered using a custom component passed to `next-mdx-remote`.

Go back [home](/).
5 changes: 5 additions & 0 deletions examples/with-mdx-remote/app/posts/hello-world.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: Hello World
---

This is an example post. There's another one [here](/example-post).
16 changes: 0 additions & 16 deletions examples/with-mdx-remote/components/CustomLink.js

This file was deleted.

49 changes: 0 additions & 49 deletions examples/with-mdx-remote/components/Layout.js

This file was deleted.

16 changes: 0 additions & 16 deletions examples/with-mdx-remote/components/TestComponent.js

This file was deleted.

7 changes: 7 additions & 0 deletions examples/with-mdx-remote/components/greet.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.div {
background-color: #111;
border-radius: 0.5em;
color: #fff;
margin-bottom: 1.5em;
padding: 0.5em 0.75em;
}
5 changes: 5 additions & 0 deletions examples/with-mdx-remote/components/greet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import styles from "./greet.module.css";

export function Greet({ name = "world" }: { name: string }) {
return <div className={styles.div}>Hello, {name}!</div>;
}
4 changes: 4 additions & 0 deletions examples/with-mdx-remote/components/mdx.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.a {
color: tomato;
text-decoration-color: rgba(0, 0, 0, 0.4);
}
Loading

0 comments on commit 9454e62

Please sign in to comment.