Skip to main content
Fumadocs supports Waku with the fumadocs-ui/provider/waku package. Waku is a minimal React framework that focuses on simplicity and performance.

Installation

1
Install Dependencies
2
npm install fumadocs-ui fumadocs-core fumadocs-mdx
3
Setup Root Layout
4
Create a root layout with the provider:
5
import type { ReactNode } from 'react';
import { Provider } from '@/components/provider';
import '@/styles/globals.css';

export default async function RootElement({ children }: { children: ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head></head>
      <body data-version="1.0">
        <Provider>{children}</Provider>
      </body>
    </html>
  );
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};
6
Create Provider Component
7
Create a client component for the RootProvider:
8
'use client';
import type { ReactNode } from 'react';
import { RootProvider } from 'fumadocs-ui/provider/waku';

export function Provider({ children }: { children: ReactNode }) {
  return <RootProvider>{children}</RootProvider>;
}
9
Create Docs Layout
10
Create a layout for documentation pages:
11
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { source } from '@/lib/source';
import { baseOptions } from '@/lib/layout.shared';

export default async function Layout({ children }: { children: React.ReactNode }) {
  return (
    <DocsLayout tree={source.getPageTree()} {...baseOptions()}>
      {children}
    </DocsLayout>
  );
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};
12
Create Catch-All Route
13
Create a catch-all route for documentation pages:
14
import { source } from '@/lib/source';
import type { PageProps } from 'waku/router';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';

export default function DocPage({ slugs }: PageProps<'/docs/[...slugs]'>) {
  const page = source.getPage(slugs);

  if (!page) {
    return (
      <div className="text-center py-12">
        <h1 className="text-3xl font-bold mb-4">Page Not Found</h1>
        <p className="text-gray-600 dark:text-gray-400">
          The page you are looking for does not exist.
        </p>
      </div>
    );
  }

  const MDX = page.data.body;
  
  return (
    <DocsPage toc={page.data.toc}>
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsDescription>{page.data.description}</DocsDescription>
      <DocsBody>
        <MDX components={{ ...defaultMdxComponents }} />
      </DocsBody>
    </DocsPage>
  );
}

export async function getConfig() {
  const pages = source
    .generateParams()
    .map((item) => (item.lang ? [item.lang, ...item.slug] : item.slug));

  return {
    render: 'static' as const,
    staticPaths: pages,
  } as const;
}

Framework Provider

The WakuProvider integrates Fumadocs with Waku’s routing system:
import { WakuProvider } from 'fumadocs-ui/provider/waku';
import { Link as WakuLink, useRouter } from 'waku';

const framework = {
  usePathname() {
    const { path } = useRouter();
    return path;
  },
  useParams() {
    const { query } = useRouter();
    
    const params = new URLSearchParams(query);
    return Object.fromEntries(
      Array.from(params.entries()).map(([key, value]) => [
        key,
        Array.isArray(value) ? value[0] : value,
      ]),
    );
  },
  useRouter() {
    const router = useRouter();

    return {
      push(url: string) {
        void router.push(url);
      },
      refresh() {
        void router.push(router.path);
      },
    };
  },
  Link({ href, prefetch = true, ...props }) {
    return (
      <WakuLink to={href!} unstable_prefetchOnEnter={prefetch} {...props}>
        {props.children}
      </WakuLink>
    );
  },
};
The provider uses:
  • useRouter().path for pathname tracking
  • router.push() for navigation
  • useRouter().query for query parameters
  • Waku’s Link component with prefetch support

Static Site Generation

Waku focuses on static site generation. Configure static paths:
export async function getConfig() {
  const pages = source
    .generateParams()
    .map((item) => (item.lang ? [item.lang, ...item.slug] : item.slug));

  return {
    render: 'static' as const,
    staticPaths: pages,
  } as const;
}
This generates static HTML for all documentation pages at build time.

Client Components

Waku requires explicit client components. Mark interactive components with 'use client':
src/components/provider.tsx
'use client';
import { RootProvider } from 'fumadocs-ui/provider/waku';

export function Provider({ children }: { children: React.ReactNode }) {
  return <RootProvider>{children}</RootProvider>;
}

Page Props

Waku provides type-safe page props:
import type { PageProps } from 'waku/router';

export default function DocPage({ slugs }: PageProps<'/docs/[...slugs]'>) {
  const page = source.getPage(slugs);
  // slugs is typed as string[]
}

Routing

Waku uses file-based routing:
  • src/pages/index.tsx/
  • src/pages/docs/_layout.tsx → Layout for /docs/*
  • src/pages/docs/[...slugs].tsx/docs/* (catch-all)

Prefetching

Waku supports prefetching on link hover:
Link({ href, prefetch = true, ...props }) {
  return (
    <WakuLink to={href!} unstable_prefetchOnEnter={prefetch} {...props}>
      {props.children}
    </WakuLink>
  );
}
Note: This uses Waku’s unstable prefetch API. The router provides navigation methods:
const router = useRouter();

// Navigate to a new page
router.push('/docs/getting-started');

// Refresh current page
router.push(router.path);

Quick Start

Use the CLI to create a new Waku project with Fumadocs:
npx create-fumadocs-app
Select “Waku” when prompted for the framework.

Build docs developers (and LLMs) love