Installation
Add fumero to a project and install the components into a docs app
Fumero is a development dependency. It runs when documentation is built and is not part of what the project ships.
uv add fumero --dev # or pip install fumerofumero --version prints what you got, and is the quickest way to tell whether the command landed
on your path at all.
The components
Generated MDX uses a small set of React components: cards for members, rows for parameters, links for types. They are installed as source rather than as a package, so a docs app owns them outright and no release overwrites local changes.
fumero init src/components/mdxwrote src/components/mdx/pdx-components.tsx
wrote src/components/mdx/pdx-plugin.tsxThe argument is a directory, wherever the app keeps its MDX components. Fumero creates it if it is missing and overwrites the two files it owns inside it.
Two files go out, because they are registered in different places. pdx-components.tsx renders the
pages. pdx-plugin.tsx is a Fumadocs loader plugin, which works on the page tree the sidebar is
built from rather than on any page, and labels the entries that lead to a module.
Registering them
Fumadocs renders MDX with the components it is given, so the set has to be registered once:
import defaultMdxComponents from 'fumadocs-ui/mdx';
import type { MDXComponents } from 'mdx/types';
import * as Pdx from '@/components/mdx/pdx-components';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultMdxComponents,
...Pdx,
...components,
};
}Without the registration, generated pages render their tags as literal text.
The sidebar labels
A generated page says what it is in its frontmatter, _fumero: { kind: module }, and the plugin
turns that into a label on the sidebar entry leading to it. Only the modules are labelled, in green,
since a module is where the tree opens. It goes on the loader, beside whatever plugins are already
there:
import { docs } from '@/.source/server';
import { loader } from 'fumadocs-core/source';
import { fumeroPlugin } from '@/components/mdx/pdx-plugin';
export const source = loader({
plugins: [fumeroPlugin()],
baseUrl: '/docs',
source: docs.toFumadocsSource(),
});Fumadocs keeps only the frontmatter its schema names, so let _fumero through:
import { defineDocs } from 'fumadocs-mdx/config';
import { pageSchema } from 'fumadocs-core/source/schema';
export const docs = defineDocs({
docs: {
schema: pageSchema.loose(),
},
});Or name the key, to have it typed:
schema: pageSchema.extend({ _fumero: z.record(z.string(), z.custom()).optional() }),Everything fumero carries into the page tree arrives under _fumero, so this is the only schema edit
there will be. Without it the pages render as they otherwise would, and the sidebar says nothing
about kinds.
The words and their tints are the KINDS table at the top of pdx-plugin.tsx, and a kind left out
of it goes unlabelled. Adding class there labels the class pages too, since their frontmatter
already carries kind: class.
renderLabel words a label differently or drops one. The row places whatever it returns:
fumeroPlugin({
renderLabel: (kind) => (kind === 'module' ? <span className="font-mono text-xs">mod</span> : undefined),
});