Docs Site (Fumadocs)
The documentation site built with Fumadocs, Next.js, and Tailwind CSS.
YeetCode ships with a documentation site built on Fumadocs — a modern, TypeScript-first documentation framework built on Next.js. It lives in apps/docs/ and runs on port 3001.
Why Fumadocs
- Next.js native — It's just a Next.js app, not a separate static site generator
- MDX content — Write docs in MDX with full React component support
- Built-in search — Full-text search powered by Orama (no external service needed)
- OG image generation — Auto-generated Open Graph images for social sharing
- LLM-friendly endpoints —
/llms.txtand/llms-full.txtserve docs in formats optimized for AI tools - Type-safe — Content collections with Zod schema validation
App Structure
apps/docs/
├── app/
│ ├── layout.tsx # Root layout (RootProvider, Inter font)
│ ├── global.css # Tailwind + Fumadocs CSS imports
│ ├── (home)/
│ │ ├── layout.tsx # Home layout (HomeLayout)
│ │ └── page.tsx # Landing page
│ ├── docs/
│ │ ├── layout.tsx # Docs layout (DocsLayout + sidebar)
│ │ └── [[...slug]]/page.tsx # Dynamic page renderer
│ ├── api/search/route.ts # Full-text search endpoint (Orama)
│ ├── og/docs/[...slug]/route.tsx # OG image generation
│ ├── llms.txt/route.ts # LLM page index
│ ├── llms-full.txt/route.ts # LLM full docs dump
│ └── llms.mdx/docs/[[...slug]]/route.ts # Raw MDX per page
├── content/docs/ # MDX documentation source
├── components/ai/page-actions.tsx # AI tool integration buttons
├── lib/
│ ├── source.ts # Content loader (Fumadocs Source API)
│ ├── layout.shared.tsx # Shared nav config (title, GitHub link)
│ └── cn.ts # Tailwind class merge utility
├── source.config.ts # Fumadocs MDX collection config
├── mdx-components.tsx # MDX component overrides
├── next.config.mjs # Next.js + Fumadocs MDX plugin
└── postcss.config.mjs # Tailwind CSS v4Content Authoring
Writing Pages
Create .mdx files in content/docs/. Each file needs frontmatter:
---
title: My Page Title
description: A brief description for metadata and search.
---
Your content here. Full MDX — use React components, code blocks, tables, etc.Navigation Order
Control sidebar order in content/docs/meta.json:
{
"pages": [
"index",
"getting-started",
"---Section Title---",
"page-slug"
]
}- File names (without
.mdx) become slugs ---Text---entries create section separators in the sidebar
MDX Components
All default Fumadocs UI components are available in MDX (Cards, Card, Callout, Steps, Tabs, etc.). Custom components can be added in mdx-components.tsx:
import defaultMdxComponents from "fumadocs-ui/mdx";
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultMdxComponents,
...components,
};
}Content Loader
The Source API in lib/source.ts loads and indexes all MDX content:
import { docs } from "fumadocs-mdx:collections/server";
import { loader } from "fumadocs-core/source";
import { lucideIconsPlugin } from "fumadocs-core/source/lucide-icons";
export const source = loader({
baseUrl: "/docs",
source: docs.toFumadocsSource(),
plugins: [lucideIconsPlugin()],
});This provides:
source.getPages()— All pagessource.getPage(slug)— Single page by slugsource.getPageTree()— Navigation tree for the sidebarsource.generateParams()— Static params forgenerateStaticParams()
MDX Collection Config
source.config.ts defines how content is loaded:
import { metaSchema, pageSchema } from "fumadocs-core/source/schema";
import { defineConfig, defineDocs } from "fumadocs-mdx/config";
export const docs = defineDocs({
dir: "content/docs",
docs: {
schema: pageSchema,
postprocess: {
includeProcessedMarkdown: true, // Enables LLM text extraction
},
},
meta: {
schema: metaSchema,
},
});The includeProcessedMarkdown option stores processed markdown alongside each page, which powers the LLM endpoints and copy-to-clipboard feature.
Layouts
Root Layout
Wraps the entire app with Fumadocs RootProvider (handles theme, search, and context):
import { RootProvider } from "fumadocs-ui/provider/next";
export default function Layout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<RootProvider>{children}</RootProvider>
</body>
</html>
);
}Docs Layout
The /docs route uses DocsLayout which renders the sidebar from the page tree:
import { DocsLayout } from "fumadocs-ui/layouts/docs";
export default function Layout({ children }) {
return (
<DocsLayout tree={source.getPageTree()} {...baseOptions()}>
{children}
</DocsLayout>
);
}Shared Config
Navigation title and GitHub link are shared across layouts via lib/layout.shared.tsx:
export const gitConfig = {
user: "lyemac",
repo: "yeetcode",
branch: "main",
};
export function baseOptions() {
return {
nav: { title: "YeetCode" },
githubUrl: `https://github.com/${gitConfig.user}/${gitConfig.repo}`,
};
}Search
Full-text search is powered by Orama and runs entirely client-side after an initial index fetch. The search API is at app/api/search/route.ts:
import { createFromSource } from "fumadocs-core/search/server";
export const { GET } = createFromSource(source, {
language: "english",
});No external search service needed — Fumadocs builds the search index at build time and serves it via this route.
OG Image Generation
Every docs page gets an auto-generated Open Graph image at /og/docs/[...slug]/image.png. These are generated using Next.js ImageResponse with Fumadocs' default image template:
import { generate as DefaultImage } from "fumadocs-ui/og";
return new ImageResponse(
<DefaultImage
title={page.data.title}
description={page.data.description}
site="YeetCode"
/>,
{ width: 1200, height: 630 },
);The page's generateMetadata function references these automatically.
LLM-Friendly Endpoints
The docs site exposes three endpoints for AI tools:
| Endpoint | Description |
|---|---|
/llms.txt | Page index — title, URL, and description for every page |
/llms-full.txt | Full dump — all processed markdown concatenated |
/docs/:path.mdx | Per-page raw markdown (rewrites to /llms.mdx/docs/:path) |
The .mdx rewrite is configured in next.config.mjs:
async rewrites() {
return [
{ source: '/docs/:path*.mdx', destination: '/llms.mdx/docs/:path*' },
];
}AI Page Actions
Every docs page includes two interactive components (from components/ai/page-actions.tsx):
Copy Markdown Button
Fetches the raw MDX from the page's .mdx URL and copies it to clipboard. Includes client-side caching so repeated copies are instant.
Open In Dropdown
A dropdown with links to open the page's content in:
- GitHub — Source file on GitHub
- Scira AI — AI search tool
- ChatGPT — Opens ChatGPT with the page content as context
- Claude — Opens Claude with the page content as context
- Cursor — Opens Cursor IDE with the page as a prompt
Each link constructs a query string that passes the full markdown URL to the AI tool.
Styling
Tailwind CSS v4 (PostCSS-based) with Fumadocs' neutral theme:
/* global.css */
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';The cn() utility from lib/cn.ts re-exports twMerge for merging Tailwind classes.
Commands
bun run dev # Dev server at localhost:3001
bun run build # Production build
bun run start # Start production server
bun run typecheck # Generates .source files, then type-checks
bun run lint # Lint with Biome
bun run format # Format with Biome
bun run clean # Removes .turbo, node_modules, .next, .sourceNote: typecheck runs fumadocs-mdx first (generates .source/ collection files) before running tsc. The postinstall script also runs fumadocs-mdx so collections are generated on bun install.
Adding a New Page
- Create
content/docs/my-page.mdxwith frontmatter - Add
"my-page"to thepagesarray incontent/docs/meta.json - The page is automatically available at
/docs/my-pagewith search, OG images, and LLM endpoints