Next.js
does not require any additional setup and should work out of the box.
Dynamic RSC page
are ideal for the purpose of bundling documents because they render in a server context.
Bundling documents
import { docs } from 'mdx-butler';
import { cache } from 'react';
export const getDocs = cache(() =>
docs({
fields: {
title: {
required: true,
},
},
})
);
Static Site Generation
Export a generateStaticParams
function to use dynamic routing and Static Site Generation ().
export async function generateStaticParams() {
const docs = await getDocs();
return docs.map((x) => ({
slug: x.path.split('/'),
}));
}
Exporting Metadata
export async function generateMetadata({
params: { slug },
}: {
params: { slug: string[] };
}) {
const docs = await getDocs();
const path = slug.join('/');
const doc = docs.find((x) => path === x.path);
return {
title: doc?.frontmatter.title,
};
}
Creating the Page
import { Component } from 'mdx-butler/client';
// ...
export default async function Docs({
params: { slug },
}: {
params: { slug: string[] };
}) {
const docs = await getDocs();
const path = slug.join('/');
const doc = docs.find((x) => path === x.path);
if (!doc) return <div>not found</div>;
return (
<div
style={{
display: 'flex',
gap: '1rem',
flexDirection: 'row',
}}
>
<div>
<h1>{doc.frontmatter.title}</h1>
<Component doc={doc} />
</div>
<div
style={{
display: 'flex',
gap: '1rem',
flexDirection: 'column',
}}
>
<h2>On this page</h2>
{doc.headings.map((x) => (
<a key={x.title} href={`#${x.title}`}>
{x.title}
</a>
))}
</div>
</div>
);
}
Customization/Optimization
For more ambitious use-cases and for filtering that depends on the Frontmatter
object, the getDocs
function can be adjusted to use a for filtering.
import { docs, createFrontmatterProcessor } from 'mdx-butler';
import { cache } from 'react';
type Frontmatter = {
title: string;
description: string;
};
const getDocs = cache((path?: string) =>
docs<Frontmatter>({
frontmatterProcessor: (x) => {
const processor = createFrontmatterProcessor<Frontmatter>({
title: {
required: true,
},
description: {},
});
return (processor(x) && path == undefined) || path === x.path;
},
})
);
// ...
export default async function Docs({
params: { slug },
}: {
params: { slug: string[] };
}) {
const path = slug.join('/');
const docs = await getDocs(path);
const doc = docs.find((x) => path === x.path);
if (!doc) return <div>not found</div>;
// ...
}