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.jsonfiles. - 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
- Create
content/docs/SECTION/SLUG.mdxwith frontmatter:--- title: My new page description: Short summary --- Body… - Add the page to the section's
meta.jsonpagesarray. - Save. The dev server rebuilds.
Adding a section
mkdir content/docs/new-section/- Add
meta.jsonand child mdx files. - Reference in root
content/docs/meta.jsonvia"...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.