Fumadocs

Generating Pages

Generate pages from AsyncAPI schema.

staticSource() and dynamicSource() generate pages for your loader(), generateFiles() writes them into your content directory as MDX files instead. They accept the same options.

Note that the page content is still rendered by the <AsyncAPIPage /> component, the docs generator only arranges the operations to render for each page.

Options

per

Customize the content of pages, default to operation.

Operation refers to an AsyncAPI operation with a specific action like receiveLightMeasurement:receive.

modecontentfile path
tagoperations with same tag{tag_name}.mdx
fileoperations in same schema file{file_name}.mdx
operationeach operation{operationId}.mdx
customsee belowN/A
import { asyncapi } from '@/lib/asyncapi';

await asyncapi.staticSource({
  per: 'tag',
});

When set to custom, you can pass a function to fully customize the generation process:

Example
import { asyncapi } from '@/lib/asyncapi';

await asyncapi.staticSource({
  per: 'custom',
  toPages(builder) {
    for (const [id, item] of Object.entries(builder.document.operations ?? {})) {
      const operation = builder.dereferenceShallow(item);

      builder.create({
        type: 'operation',
        schemaId: builder.id,
        item: { id, action: operation.action },
        path: `${id}.mdx`,
        info: {
          title: operation.title ?? id,
          description: operation.description,
        },
      });
    }
  },
});

When set to tag, tags are matched by name. Define them in info.tags to give their pages a description, and x-displayName to control the title. Operations without tags are under unknown.

asyncapi.yaml
info:
  tags:
    - name: lighting
      description: Operations related to streetlights.
      x-displayName: Lighting

groupBy

In operation mode, you can group output files with folders.

valueoutput
tag{tag}/{operationId}.mdx
channel{channel key}/{operationId}.mdx
none (default){operationId}.mdx
import { asyncapi } from '@/lib/asyncapi';

await asyncapi.staticSource({
  per: 'operation',
  groupBy: 'tag',
});

name

A function that controls the file name of generated pages.

import { asyncapi } from '@/lib/asyncapi';

await asyncapi.staticSource({
  name(output) {
    if (output.type === 'operation') {
      const operation = this.dereferenceShallow(this.document.operations?.[output.item.id]);
      console.log(operation);

      return `my-dir/${output.item.id}`;
    }

    return 'my-dir/filename';
  },
});

You can also use the algorithm option:

import { asyncapi } from '@/lib/asyncapi';

await asyncapi.staticSource({
  // v2 (default): use operation id
  // v1: use `{operationId}/{action}`
  name: { algorithm: 'v1' },
});

meta

Generate meta.json files automatically.

You can always define the meta.json files manually for maximal flexibility.

import { asyncapi } from '@/lib/asyncapi';

await asyncapi.staticSource({
  meta: true,
});

Or customize how folders are represented:

import { asyncapi } from '@/lib/asyncapi';

await asyncapi.staticSource({
  meta: {
    folderStyle: 'separator',
  },
});

MDX Files

To write pages as MDX files, create a script:

scripts/generate-docs.ts
import { generateFiles } from '@fumadocs/asyncapi';
import { asyncapi } from '@/lib/asyncapi';

void generateFiles({
  input: asyncapi,
  output: './content/docs',
  // we recommend to enable it
  // make sure your operation description doesn't break MDX syntax.
  includeDescription: true,
});

Generate docs with the script:

bun ./scripts/generate-docs.ts

Add the AsyncAPIPage component to your MDX components. Generated files use the <AsyncAPIPage /> tag.

app/docs/[[...slug]]/page.tsx
import { asyncapi } from '@/lib/asyncapi';
import { AsyncAPIPage } from '@/components/api-page';

export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
  // ...
  <MDX
    components={getMDXComponents({
      AsyncAPIPage: async (props) => (
        <AsyncAPIPage {...await asyncapi.preloadAsyncAPIPage(page)} {...props} />
      ),
    })}
  />;
}

Add the plugin to your loader():

lib/source.ts
import { asyncapiPlugin } from '@fumadocs/asyncapi/server';
import { loader } from 'fumadocs-core/source';

export const source = loader({
  // optional: adds the direction of messages to each page item in page tree
  plugins: [asyncapiPlugin()],
});

generateFiles() also accepts the options below.

index

Generate index files with cards linking to generated pages.

import { generateFiles } from '@fumadocs/asyncapi';

void generateFiles({
  index: {
    // for generating `href`
    url: {
      baseUrl: '/docs',
      contentDir: './content/docs',
    },
    items: [
      {
        // output path
        path: 'index.mdx',
        // optional: restrict the input files (identical to the `input` field in server)
        only: ['streetlights.yaml'],
        // optional: set frontmatter
        description: 'All available pages',
      },
    ],
  },
});

imports

Add custom imports to generated MDX files. This is useful for including components, utilities, or other dependencies in your generated documentation.

import { generateFiles } from '@fumadocs/asyncapi';

void generateFiles({
  imports: [
    {
      names: ['API_BASE_URL'],
      from: '@/constants',
    },
  ],
});

This will add the following imports to all generated MDX files:

import { API_BASE_URL } from '@/constants';

frontmatter

Customize the frontmatter of MDX files.

By default, it includes:

propertydescription
titlePage title
descriptionPage description
fullAlways true, added for Fumadocs UI
_asyncapiInternal Properties
import { generateFiles } from '@fumadocs/asyncapi';

void generateFiles({
  frontmatter: (title, description) => ({
    myProperty: 'hello',
  }),
});

addGeneratedComment

Add a comment to the top of generated files indicating they are auto-generated.

import { generateFiles } from '@fumadocs/asyncapi';

void generateFiles({
  // Add default comment
  addGeneratedComment: true,

  // Or provide a custom comment
  addGeneratedComment: 'Custom auto-generated comment',

  // Or disable comments
  addGeneratedComment: false,
});

How is this guide?

Last updated on