github fuma-nama/fumadocs fumadocs@16.15.3

4 hours ago
  • @fumadocs/base-ui@16.15.3
  • fumadocs-core@16.15.3
  • fumadocs-ui@16.15.3

Fix duplicated search result for pages whose description repeats in the content

Pages generated by Fumadocs OpenAPI emit the operation description as both the page description and a content section, so every endpoint page listed the same line twice in the search dialog. Search indexing now skips the page description when an identical content record exists, keeping the record with the heading anchor.

Fix #3509

Remark LLMs: export a component with output: "function"

With output: "function", _markdown becomes a component instead of a string: Markdown content is still stringified at compile time, while JSX elements stay as JSX, receiving their original props.

// fumadocs-mdx collection config
postprocess: {
  includeProcessedMarkdown: { output: 'function' },
},

Render it with renderToMarkdown from fumadocs-core/server. Elements resolve from props.components: a component can call asMarkdown() to output its own Markdown form, other components (including missing ones) are serialized as JSX syntax.

import { renderToMarkdown } from 'fumadocs-core/server';

const { _markdown: Content } = await page.data.load();
const text = await renderToMarkdown(<Content components={getMDXComponents()} />);

getText('processed') keeps working: it renders the component for you, with an optional components map:

const text = await page.data.getText('processed', { components: getMDXComponents() });

Supported in bundler collections with both compilers, and in dynamic: true collections & @fumadocs/satteri/local-md with the Sätteri compiler.

fumadocs-core/server: render React trees into Markdown

The Markdown renderer of Fumapress is now part of Fumadocs core. renderToMarkdown() converts RSC output into Markdown, and calling asMarkdown() is how a server component opts in with its own Markdown form:

import { asMarkdown, md, renderToMarkdown } from 'fumadocs-core/server';

async function Callout({ title, children }) {
  if (asMarkdown()) return md.linePrefix('> ')`**${title}**\n${children}`;

  return <div className="callout">...</div>;
}

const text = await renderToMarkdown(
  <Callout title="Note">
    <p>Hello</p>
  </Callout>,
);
// > **Note**
// >
// > Hello

Components that never call asMarkdown() are kept as JSX syntax with their serializable props, so are client components, which never run on the server. Host elements returned by an opted-in component are converted with a built-in HTML to Markdown table.

renderRoute() renders a page element only when its component opts in, for giving arbitrary routes a Markdown version. In the browser the module resolves to a stub where asMarkdown() is always false.

Don't miss a new fumadocs release

NewReleases is sending notifications on new releases.