Orientation
Content Processing
Search Infrastructure
Rendering and UI
Developer Tooling
How It Works
The following files were used as context for generating this wiki page:
The Fumadocs Command Line Interface (CLI) serves as the primary orchestration layer for initializing, configuring, and extending documentation projects. It solves the fragmentation of project scaffolding by providing a unified, interactive entry point that handles framework-specific requirements—such as template selection, dependency installation, and directory structure setup—without requiring manual boilerplate configuration.
Architecturally, the CLI is split into specialized packages: create-app for initial bootstrapping and @fumadocs/cli for ongoing project maintenance. It utilizes a plugin-based system to handle modular concerns like AI integration, linting configurations, and layout customization, ensuring that the CLI remains maintainable while supporting diverse frontend ecosystems (Next.js, Waku, React Router, and Tanstack Start).
Beyond scaffolding, the CLI provides deep operational utilities for managing UI components. It interacts with registries to fetch, install, and update UI components into a local project while strictly respecting user configuration defined in cli.json. By providing interactive prompts, the CLI abstracts complex configuration choices into a coherent user journey, ensuring consistent project standards across the Fumadocs ecosystem.
The CLI operates by parsing user inputs via commander, which then dispatches tasks to specific command modules. The core logic for project management is decentralized into specialized commands like add and customise.
Sources: packages/cli/src/index.ts:15-116, packages/create-app/src/bin.ts:56-300
The FumadocsComponentInstaller class encapsulates the logic for adding components to an existing project. It acts as the bridge between the remote component source and the local file system.
The installer uses a plugin architecture to preserve critical layout components. During installation, the pluginPreserveLayouts plugin acts as a safety gate.
// Installer configuration logic in packages/cli/src/registry/installer.ts
this.interactive = { name, spin };
const deps = await super.install(name, subRegistry).then((res) => res.deps());
spin.stop(picocolors.bold(picocolors.greenBright(`${name} installed`)));Sources: packages/cli/src/registry/installer.ts:54-65
Warning
The installer enforces a strict state invariant: it throws an error if a new installation attempt occurs while an installation is already in progress (if (this.interactive) throw new Error(...)). This prevents race conditions in file system modifications.
Sources: packages/cli/src/registry/installer.ts:55-57
The CLI maintains configuration via cli.json. The createOrLoadConfig function is the primary source of truth, performing a check to see if a file exists before parsing.
Sources: packages/cli/src/config.ts:62-70, packages/cli/src/config.ts:14-55
create-appThe project generator uses a sequential plugin pipeline to transform the initial template. Plugins can define hooks at different stages of the lifecycle.
Sources: packages/create-app/src/index.ts:64-126
The customise command allows users to replace or rewrite specific parts of the documentation UI. It dynamically fetches registry information and computes target options based on the available layouts in the registry.
The getSlotCode helper function contains the logic to generate code blocks for slot replacement. It acts as a router based on the name of the slot being customized, returning the correct import and component structure for the user's layout.tsx.
Sources: packages/cli/src/commands/customise.ts:22-179, packages/cli/src/commands/customise.ts:214-381
The tree command converts a file system structure into a representable JSON format, which can be output as MDX or JavaScript components.
Note
When generating the file tree, if a directory contains exactly one sub-item that is a name-indexed node, the command collapses the hierarchy level to reduce nesting in the final MDX output. Sources: packages/cli/src/commands/file-tree.ts:27-35
// Example call to convert a file tree node to MDX
if (item.contents.length === 1 && 'name' in item.contents[0]) {
const child = item.contents[0];
return toNode({
...child,
name: `${item.name}/${child.name}`,
});
}Sources: packages/cli/src/commands/file-tree.ts:28-34