Olympus Docs
InternalsSite

Site, Fumadocs

How the docs site is built with Fumadocs

The Olympus docs (this site you're reading) is built with Fumadocs inside the Site repo's Next.js app.

What Fumadocs gives us

  • MDX rendering with code highlighting (via Shiki).
  • Sidebar navigation driven by meta.json files.
  • Search out of the box.
  • Dark mode baked in.
  • TOC (right-rail) generated from headings.
  • MDX components can include React.

Where the docs live

site/content/docs/
├── meta.json                   # Top-level IA + separators
├── index.mdx                   # /docs landing
├── what-is-olympus.mdx
├── architecture.mdx
├── ...
├── get-started/
│   ├── meta.json
│   └── *.mdx
├── deploy/
│   ├── meta.json
│   └── *.mdx
└── ... (lifecycle sections)

meta.json controls navigation order, separators ("---Section---"), and folder globs ("...subfolder").

How pages are loaded

site/source.ts wires Fumadocs:

import { docs, meta } from "./.source/server";
import { loader } from "fumadocs-core/source";
import { toFumadocsSource } from "fumadocs-mdx/runtime/server";

const s = toFumadocsSource(docs, meta);
export const source = loader(s, { baseUrl: "/docs" });

.source/server is auto-generated by fumadocs-mdx during dev/build, it scans content/docs/ for MDX and meta.json files.

The docs page

site/src/app/docs/[[...slug]]/page.tsx:

import { source } from "../../../../source";
import { DocsPage, DocsBody, ... } from "fumadocs-ui/page";

export default async function Page(props) {
  const params = await props.params;
  const page = source.getPage(params.slug);
  if (!page) notFound();
  return (
    <DocsPage toc={page.data.toc}>
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsDescription>{page.data.description}</DocsDescription>
      <DocsBody>
        <MDX components={{ ...defaultMdxComponents, Mermaid }} />
      </DocsBody>
    </DocsPage>
  );
}

[[...slug]] is Next.js's catch-all route. Every doc URL hits this page; slug becomes the path within content/docs/.

Custom MDX components

mdx-components.tsx registers components available to MDX:

import defaultMdxComponents from "fumadocs-ui/mdx";
import { Mermaid } from "@/components/site/mermaid";

export function useMDXComponents(components) {
  return { ...defaultMdxComponents, Mermaid, ...components };
}

The Mermaid component is used to render diagrams. See Internals, site/lib/remark-mermaid.mjs.

Remark plugins

source.config.ts:

import { remarkMermaid } from "./lib/remark-mermaid.mjs";

export default defineConfig({
  mdxOptions: { remarkPlugins: [remarkMermaid] },
});

remarkMermaid transforms ```mermaid` code blocks into the Mermaid component, which the registered component renders client-side.

Generators write into content/docs/

site/scripts/gen/*.mjs produce reference pages from source:

  • Hydra OpenAPI → content/docs/reference/api/hydra/*.mdx
  • Athena routes (AST walk) → content/docs/reference/api/athena/*.mdx
  • Compose files → content/docs/reference/config/compose/*.mdx
  • ...

bun run gen is wired as a prebuild script, runs before every Next.js build.

Adding a new page

  1. Create content/docs/SECTION/SLUG.mdx with frontmatter:
    ---
    title: My new page
    description: Short summary
    ---
    Body…
  2. Add the page to the section's meta.json pages array.
  3. Save. The dev server rebuilds.

Adding a section

  1. mkdir content/docs/new-section/
  2. Add meta.json and child mdx files.
  3. Reference in root content/docs/meta.json via "...new-section" (folder include) or explicit slugs.

Performance

Fumadocs uses static generation by default. Every page is pre-rendered at build time. The build for 300+ pages takes ~30 seconds on a 4-core machine.

For new doc-only updates that don't change React code, only the changed MDX pages need to re-render. Caching is aggressive.

On this page