Fumadocs

Plugins

Add features to your site with plugins.

Overview

Plugins add features like search and LLM routes to your site. Add them to your config:

src/fumadocs.config.ts
import { defineConfig } from '@fumadocs/astro/config';
import { takumiPlugin } from '@fumadocs/astro/plugins/takumi'; 
import { source } from './lib/source';

export default defineConfig({
  source,
  plugins: [takumiPlugin()], 
});

Default Plugins

oramaSearchPlugin() and llmsPlugin() are added by default. Add one yourself to pass its options, it replaces the default one:

src/fumadocs.config.ts
import { oramaSearchPlugin } from '@fumadocs/astro/plugins/orama-search';

export default defineConfig({
  // ...
  plugins: [oramaSearchPlugin({ language: 'english' })], 
});

To remove them, set preset to false.

Writing Plugins

A plugin is an object, its hooks receive the app as this, like this.getPages() for all pages:

Prop

Type

For example, a plugin that serves the URLs of pages, and opens API pages in full width:

src/lib/my-plugin.ts
import type { Plugin } from '@fumadocs/astro/config';

export function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    routes() {
      const urls = this.getPages().map((page) => page.url);

      return [
        {
          type: 'file',
          url: `${this.base}/pages.json`,
          GET: () => Response.json(urls),
        },
      ];
    },
    transformPage(page, data) {
      return page.slugs[0] === 'api' ? { ...data, full: true } : data;
    },
  };
}

transformPage() changes the page data, received by the page module and used for <head> tags like the title.

A route of type: 'page' renders an Astro component with props, use HomeLayout.astro for the layout.

An item of plugins can also be an array of plugins, false and undefined are skipped:

plugins: [myPlugin(), import.meta.env.PROD && takumiPlugin()];

Content Sources

Your own content source renders its pages with a plugin. The built-in sources work already. getBody() returns their content as React nodes, rendered with your MDX components (this.components) so they hydrate like MDX content.

For example, pages with MDX from a CMS, compiled by MDX Remote:

src/lib/cms-plugin.ts
import type { Plugin } from '@fumadocs/astro/config';
import { createCompiler } from '@fumadocs/mdx-remote';
import { createElement } from 'react';

const compiler = createCompiler();

/** renders pages with MDX in `page.data.mdx` */
export function cmsPlugin(): Plugin {
  return {
    name: 'cms',
    async getBody(page) {
      const { mdx } = page.data as { mdx?: string };
      if (typeof mdx !== 'string') return;
      const { body, toc } = await compiler.compile({ source: mdx });
      return { node: createElement(body, { components: this.components }), toc };
    },
    getText(page) {
      const { mdx } = page.data as { mdx?: string };
      if (typeof mdx === 'string') return mdx;
    },
  };
}

Search indexes the structuredData of page data, and a lastModified date shows as the last updated time of pages.

Server Routes

Routes are generated at build time. Set prerender: false to handle requests on the server. This requires an Astro adapter:

routes() {
  return [
    {
      type: 'file',
      url: `${this.base}/api/feedback`,
      prerender: false,
      POST: async ({ request }) => Response.json(await request.json()),
    },
  ];
},

A middleware hook runs as an Astro middleware, for prerendered pages at build time too, see context.isPrerendered.

How is this guide?

Last updated on