Relevant source files
The following files were used as context for generating this wiki page:
- packages/base-ui/registry/index.ts
- packages/radix-ui/registry/index.ts
- packages/cli/src/commands/shared.ts
- packages/shadcn/src/manual-installation.ts
- packages/shadcn/src/rsc.tsx
- packages/shadcn/src/index.ts
- packages/cli/src/commands/add.ts
- packages/sanity/registry/index.ts
- packages/shadcn/package.json
- packages/radix-ui/package.json
- packages/shared/registry.ts
- packages/asyncapi/src/ui/index.tsx
- packages/cli/src/registry/installer.ts
- packages/cli/src/commands/customise.ts
- packages/preview/src/pages/...slugs.tsx
- packages/content/src/index.ts
- packages/cli/src/registry/plugins/preserve.ts
- packages/openapi/src/ui/base.tsx
- packages/base-ui/package.json
- packages/create-app/src/plugins/orama-cloud.ts
- packages/cli/src/index.ts
- packages/create-app/src/plugins/ai.ts
- packages/preview/src/components/provider.tsx
- packages/api-docs/package.json
- packages/base-ui/src/provider/waku.tsx
- packages/preview/package.json
- packages/create-app/scripts/sync.ts
- packages/core/package.json
- packages/python/src/components/index.tsx
- packages/openapi/package.json
Overview
The @fumadocs/shadcn package provides an integration layer that connects shadcn UI registry definitions with Fumadocs documentation projects. It resolves component files, dependencies, and configuration snippets for manual or automated installation.
Sources: packages/shadcn/package.json:1-8, packages/shadcn/src/index.ts:10-24
By parsing built registry items and processing component collections across multiple UI libraries like fumadocs/base-ui and fumadocs/radix-ui, it enables seamless component discovery and code generation.
Sources: packages/base-ui/registry/index.ts:9-14, packages/radix-ui/registry/index.ts:9-14, packages/shadcn/src/manual-installation.ts:58-64
Registry Item Schema and Definitions
Built Registry Structure and Component Metadata
Documentation and UI components within the integration are modeled through built registry item types, file specifications, and common utility definitions. Each entry in the registry maps specific file paths, targets, and internal component types such as layout slots or shared utilities.
| Component / File Type | Identifier / Path | Target Location | Description |
|---|---|---|---|
| Slot Component | slots/* | <dir>/layouts/**/slots/* | Discovered layout slot components from layout directories |
| URL Utility | urls | utils/urls.ts | Common URL helper utility |
| Class Name Utility | cn | utils/cn.ts | Class merging utility |
| Reference Merger | merge-refs | utils/merge-refs.ts | DOM ref merging utility |
| Table of Contents | toc | <dir>/toc/*.tsx | Clerk, default, and index Table of Contents components |
Sources: packages/shared/registry.ts:5-83
Registry Types and Manual Installation Snippets
The manual installation system defines union types for generated documentation snippets, file payloads, and package manager commands. Snippets are categorized by their kind, determining how they render in documentation views.
export type ManualInstallationSnippet =
| {
lang: string;
code: string;
path: string;
title: string;
kind: 'file';
}
| {
kind: 'docs';
content: string;
}
| {
kind: 'cssVars' | 'css' | 'tailwind' | 'envVars';
lang: string;
code: string;
}
| {
kind: 'dependencies' | 'devDependencies' | 'registryDependencies';
lang: string;
codeTabs: Record<PM, string>;
dependencies: string[];
};Sources: packages/shadcn/src/manual-installation.ts:12-42
Note
The includeRegistryDependencies option defaults to true when fetching installation options, automatically queueing and resolving local registry dependencies alongside primary items.
Sources: packages/shadcn/src/manual-installation.ts:51-63
Component Discovery Across UI Libraries
Glob Discovery and Slot Collection
The shared registry logic uses tinyglobby to scan the local filesystem for layout slot components within layout subdirectories. By executing a glob pattern against the target components directory, it discovers custom layout slots and dynamically constructs corresponding registry components with unlisted flags.
export async function findSlotComponents(dir: string): Promise<Component[]> {
const slots: Component[] = [];
for (const file of await glob('layouts/**/slots/*', { cwd: dir })) {
const relativePath = path.relative('layouts', file);
const name = relativePath
.split(path.sep)
.filter((v) => v !== 'slots')
.join('/')
.slice(0, -path.extname(relativePath).length);
slots.push({
name: `slots/${name}`,
unlisted: true,
files: [
{
path: file,
type: 'layout',
target: path.join('<dir>', relativePath),
},
],
});
}
return slots;
}Sources: packages/shared/registry.ts:1-30
Note
The dynamic slot discovery function transforms file paths by removing intermediate segment markers such as slots and stripping file extensions, producing standardized slot identifiers like slots/docs/header.
Sources: packages/shared/registry.ts:8-15
Library Registry Integration
Both the fumadocs/base-ui and fumadocs/radix-ui registries resolve their base component directory relative to the current file URL using Node's path utilities. Each library registry aggregates common shared utilities, dynamically evaluated slot components from findSlotComponents, and unlisted layout groups or individual UI components.
| Registry Package | Registry Name Property | Base Source Directory (dir) | Core Shared Components Included |
|---|---|---|---|
packages/base-ui/registry/index.ts | fumadocs/base-ui | ../src | urls, cn, merge-refs, toc |
packages/radix-ui/registry/index.ts | fumadocs/radix-ui | ../src | urls, cn, merge-refs, toc |
Sources: packages/base-ui/registry/index.ts:1-14, packages/radix-ui/registry/index.ts:1-14, packages/shared/registry.ts:32-83
Warning
In the shadcn CLI integration, the exact order of entries within the component array matters because it dictates the sequence in which import paths and dependencies are written to the consumer codebase.
Sources: packages/base-ui/registry/index.ts:8, packages/radix-ui/registry/index.ts:8
Manual Installation Pipeline and Formatting
Resolving Dependencies and Collecting Items
The manual installation pipeline is initialized via createShadcnDocs, which binds a RegistryContext containing the registryPath and root directory. When getManualInstallation executes with a given registry item name, it delegates to collectItems to recursively traverse and resolve local dependencies, accumulating items into a collection queue.
export async function getManualInstallation(
this: RegistryContext,
options: GetManualInstallationOptions,
): Promise<ManualInstallationSnippet[]> {
const { name, includeRegistryDependencies = true } = options;
const items = await collectItems.call(this, name, includeRegistryDependencies);
if (items.length === 0) return [];
const snippets: ManualInstallationSnippet[] = [];
const dependencies = new Set<string>();
const devDependencies = new Set<string>();
const registryDependencies = new Set<string>();Sources: packages/shadcn/src/manual-installation.ts:58-69
Warning
If a registry item file cannot be read from the filesystem during queue traversal, collectItems catches the error and assigns a null value in the lookup map, safely skipping broken or missing paths without halting execution.
Sources: packages/shadcn/src/manual-installation.ts:141-158
Formatting Snippets and Display Paths
Once item collection finishes, the pipeline iterates through each registry item to extract documentation notes, environment variables, CSS variables, CSS rules, Tailwind configurations, and constituent file contents. File display paths are normalized according to their specific registry type classification.
| Registry File Type | Default Target Path Prefix | Example Resolved Output |
|---|---|---|
registry:ui | components/ui/ | components/ui/button.tsx |
registry:component | components/ | components/card.tsx |
registry:hook | lib/hooks/ | lib/hooks/use-mobile.ts |
registry:lib | lib/ | lib/utils.ts |
registry:block | components/ | components/hero.tsx |
Sources: packages/shadcn/src/manual-installation.ts:218-236
Tip
If a registry file defines an explicit target property starting with ~/, the leading tilde and slash are sliced off to produce a clean relative display path for the generated code snippet.
Sources: packages/shadcn/src/manual-installation.ts:218-221
Server Components for Code Snippets
Rendering Syntax-Highlighted Code Blocks and Documentation Notes
The Snippet asynchronous React Server Component handles the presentation of individual ManualInstallationSnippet objects by evaluating their item types and rendering appropriate UI elements. When an item has a kind of 'docs', it bypasses syntax highlighting and outputs the descriptive text directly inside a paragraph styled with text colors and preserved whitespace.
export async function Snippet({ item }: { item: ManualInstallationSnippet }) {
if (item.kind === 'docs') {
return <p className="text-fd-muted-foreground text-sm whitespace-pre-wrap">{item.content}</p>;
}Sources: packages/shadcn/src/rsc.tsx:28-31
Note
Standard code snippets receive syntax highlighting through ServerCodeBlock alongside an array of pre-configured Shiki transformers consisting of transformerNotationDiff() and transformerIcon().
Sources: packages/shadcn/src/rsc.tsx:10-12, packages/shadcn/src/rsc.tsx:59-66
File Title Mapping and Dependency Tabs
To provide contextual headers for standard code snippets, the getTitle helper function inspects item kinds and maps them to fixed filenames or dynamic file paths. For dependency and package installation snippets containing multiple tabs, Snippet renders interactive code block tabs via CodeBlockTabs and CodeBlockTabsTrigger components.
| Item Kind | Mapped Title or Filename |
|---|---|
file | item.path |
cssVars / css | globals.css |
envVars | .env.local |
tailwind | tailwind.config.js |
Sources: packages/shadcn/src/rsc.tsx:14-26, packages/shadcn/src/rsc.tsx:57-65
switch (item.kind) {
case 'dependencies':
case 'devDependencies':
case 'registryDependencies':
const tabs = Object.entries(item.codeTabs);
return (
<CodeBlockTabs defaultValue={tabs[0][0]}>
<CodeBlockTabsList>
{tabs.map(([t]) => (
<CodeBlockTabsTrigger key={t} value={t}>
{t}
</CodeBlockTabsTrigger>
))}
</CodeBlockTabsList>
{tabs.map(([k, v]) => (
<CodeBlockTab key={k} value={k}>
<ServerCodeBlock code={v} lang={item.lang} />
</CodeBlockTab>
))}
</CodeBlockTabs>
);
}Sources: packages/shadcn/src/rsc.tsx:33-55
Tip
Dependency and devDependency code blocks utilize Object.entries(item.codeTabs) to populate multi-package installer tabs, setting the default active tab to the first entry in the collection.
Sources: packages/shadcn/src/rsc.tsx:37-40
CLI Automated Installation Integration
UI Registry Endpoint Mapping and Client Resolution
The Fumadocs CLI defines default UI registry mappings in shared configuration modules, mapping identifiers such as 'base-ui' to 'fumadocs/base-ui' and 'radix-ui' to 'fumadocs/radix-ui'.
Sources: packages/cli/src/commands/shared.ts:1-4
During command execution, the createClientFromDir utility function resolves registry directory parameters against predefined shortcuts, instantiating either an HttpRegistryConnector or a LocalRegistryConnector depending on whether the target path begins with an HTTP protocol schema.
| Shortcut Key | Resolved URL Target |
|---|---|
:preview | https://preview.fumadocs.dev/registry |
:dev | http://localhost:3000/registry |
| default | https://fumadocs.dev/registry |
Sources: packages/cli/src/index.ts:39-42, packages/cli/src/index.ts:108-114
Sources: packages/cli/src/index.ts:108-114
Template Plugin and Installer Integration
The application creation toolkit integrates registry components directly into newly scaffolded projects via asynchronous template plugins. For instance, the AI search plugin initializes a FumadocsComponentInstaller coupled with an HttpRegistryConnector pointing to the main registry endpoint to fetch and install required components like ai/${provider}.
export function ai(provider: 'openrouter' | 'llmgateway' | 'inkeep'): TemplatePlugin {
return {
async afterWrite() {
const config = await getDefaultConfig(this.dest);
const installer = new FumadocsComponentInstaller(
new HttpRegistryConnector('https://fumadocs.dev/registry'),
config,
this.dest,
);
try {
const deps = await installer.install(`ai/${provider}`).then((res) => res.deps());
if (deps.hasRequired()) await deps.writeRequired();
await addAIChat(this);
await fs.writeFile(path.join(this.dest, '.env.local'), `${envKey[provider]}=`);
} catch (e) {
console.error(e);
}
},
};
}Sources: packages/create-app/src/plugins/ai.ts:16-37
Note
Environment variables for AI providers are programmatically mapped through the envKey record, linking 'openrouter', 'llmgateway', and 'inkeep' directly to OPENROUTER_API_KEY, LLM_GATEWAY_API_KEY, and INKEEP_API_KEY respectively.
Sources: packages/create-app/src/plugins/ai.ts:10-14
Layout preservation plugins intercept component installation stacks to maintain references to standard layout specifications unless explicitly requested via direct installation. The pluginPreserveLayouts hook filters out child sub-components matching predefined layout names and rewrites local file imports to point directly to package-level UI modules like fumadocs-ui/layouts/docs.
export function pluginPreserveLayouts(): InstallerPlugin {
const layoutNames = [
'layouts/home',
'layouts/flux',
'layouts/notebook',
'layouts/docs',
'layouts/shared',
];Sources: packages/cli/src/registry/plugins/preserve.ts:6-13, packages/cli/src/registry/plugins/preserve.ts:43-50
Related
On This Page
OverviewRegistry Item Schema and DefinitionsBuilt Registry Structure and Component MetadataRegistry Types and Manual Installation SnippetsComponent Discovery Across UI LibrariesGlob Discovery and Slot CollectionLibrary Registry IntegrationManual Installation Pipeline and FormattingResolving Dependencies and Collecting ItemsFormatting Snippets and Display PathsServer Components for Code SnippetsRendering Syntax-Highlighted Code Blocks and Documentation NotesFile Title Mapping and Dependency TabsCLI Automated Installation IntegrationUI Registry Endpoint Mapping and Client ResolutionTemplate Plugin and Installer IntegrationRelated