Skip to main content
doc0blade47/fumadocsdocumentation home
Sign in

shadcn/ui Docs Registry

Last updated Oct 12, 2026100% grounded
Relevant source files

The following files were used as context for generating this wiki page:

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 TypeIdentifier / PathTarget LocationDescription
Slot Componentslots/*<dir>/layouts/**/slots/*Discovered layout slot components from layout directories
URL Utilityurlsutils/urls.tsCommon URL helper utility
Class Name Utilitycnutils/cn.tsClass merging utility
Reference Mergermerge-refsutils/merge-refs.tsDOM ref merging utility
Table of Contentstoc<dir>/toc/*.tsxClerk, 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 PackageRegistry Name PropertyBase Source Directory (dir)Core Shared Components Included
packages/base-ui/registry/index.tsfumadocs/base-ui../srcurls, cn, merge-refs, toc
packages/radix-ui/registry/index.tsfumadocs/radix-ui../srcurls, 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 TypeDefault Target Path PrefixExample Resolved Output
registry:uicomponents/ui/components/ui/button.tsx
registry:componentcomponents/components/card.tsx
registry:hooklib/hooks/lib/hooks/use-mobile.ts
registry:liblib/lib/utils.ts
registry:blockcomponents/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 KindMapped Title or Filename
fileitem.path
cssVars / cssglobals.css
envVars.env.local
tailwindtailwind.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 KeyResolved URL Target
:previewhttps://preview.fumadocs.dev/registry
:devhttp://localhost:3000/registry
defaulthttps://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

Was this page helpful?