YeetCode
Docs App

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.txt and /llms-full.txt serve 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 v4

Content 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.

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 pages
  • source.getPage(slug) — Single page by slug
  • source.getPageTree() — Navigation tree for the sidebar
  • source.generateParams() — Static params for generateStaticParams()

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}`,
  };
}

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:

EndpointDescription
/llms.txtPage index — title, URL, and description for every page
/llms-full.txtFull dump — all processed markdown concatenated
/docs/:path.mdxPer-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, .source

Note: 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

  1. Create content/docs/my-page.mdx with frontmatter
  2. Add "my-page" to the pages array in content/docs/meta.json
  3. The page is automatically available at /docs/my-page with search, OG images, and LLM endpoints

On this page