Fumadocs

Without Config

Use the integration with your own routes and layouts.

Overview

Without src/fumadocs.config.ts, the integration only processes your MDX files and renders their content as a React island. You write the routes, layouts and providers.

Prefer the Fumadocs config, use this only when it doesn't cover your case yet, like routes rendered on demand, or integrations without a plugin like AsyncAPI. The layout, page and css options and plugins require the config file.

Setup

Follow Manual Installation until the source files, then register your MDX components with DocsBody as wrapper for content styles:

import defaultMdxComponents from 'fumadocs-ui/mdx';
import { DocsBody } from 'fumadocs-ui/layouts/docs/page'; 
import type { MDXComponents } from 'mdx/types';

export function getMDXComponents(components?: MDXComponents) {
  return {
    ...defaultMdxComponents,
    ...components,
  } satisfies MDXComponents;
}

export default getMDXComponents({ wrapper: DocsBody }); 

Provider

Fumadocs UI needs a provider in each React island:

src/components/provider.tsx
import { navigate } from 'astro:transitions/client';
import type { ReactNode } from 'react';
import { RootProvider } from 'fumadocs-ui/provider/astro';

export interface ProviderProps {
  /** `Astro.url.pathname` */
  pathname: string;
  /** `Astro.params` */
  params: Record<string, string | undefined>;
  children?: ReactNode;
}

export function Provider({ pathname, params, children }: ProviderProps) {
  return (
    <RootProvider pathname={pathname} params={params} navigate={navigate}>
      {children}
    </RootProvider>
  );
}

Docs Page

Render the docs layout as an island, with the content as children:

import { type SerializedPageTree, useFumadocsLoader } from 'fumadocs-core/source/client';
import type { TOCItemType } from 'fumadocs-core/toc';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { Provider, type ProviderProps } from '@/components/provider';

export interface DocsProps extends ProviderProps {
  tree: SerializedPageTree;
  title: string;
  description?: string;
  toc: TOCItemType[];
}

export function Docs({ tree, title, description, toc, children, ...props }: DocsProps) {
  const data = useFumadocsLoader({ tree });

  return (
    <Provider {...props}>
      <DocsLayout nav={{ title: 'My App' }} tree={data.tree}>
        <DocsPage toc={toc}>
          <DocsTitle>{title}</DocsTitle>
          <DocsDescription>{description}</DocsDescription>
          {children}
        </DocsPage>
      </DocsLayout>
    </Provider>
  );
}

Export the search index, the search dialog queries /api/search by default:

src/pages/api/search.ts
import type { APIRoute } from 'astro';
import { createFromSource } from 'fumadocs-core/search/server';
import { source } from '@/lib/source';

const server = createFromSource(source);

export const GET: APIRoute = () => server.staticGET();

Pass a static search dialog to RootProvider, see Search UI.

Content Island

The content is its own React island, so your layout's providers don't reach it. wrapper receives <Content />'s props, add a provider there, without theme and search:

import { navigate } from 'astro:transitions/client';
import { RootProvider } from 'fumadocs-ui/provider/astro';
import type { ProviderProps } from '@/components/provider';

export default getMDXComponents({
  wrapper: ({ pathname, params, children }: ProviderProps) => (
    <RootProvider
      pathname={pathname}
      params={params}
      navigate={navigate}
      theme={{ enabled: false }}
      search={{ enabled: false }}
    >
      <DocsBody>{children}</DocsBody>
    </RootProvider>
  ),
});

How is this guide?

Last updated on