# AI agents (/docs/ai-agents)

> Help coding agents use docscn, with the docscn skill and the docs as Markdown.

## The docscn skill [#the-docscn-skill]

docscn has an [agent skill](https://agentskills.io) for coding agents such as Claude Code, Cursor and Codex. It tells the agent how to set up docs in a shadcn/ui project, migrate from Fumadocs UI, add the AI chat, and follow docscn's conventions: the `@/components/docs/` import paths, shadcn/ui theme tokens instead of Fumadocs UI's, and editing components instead of using `slots`. For anything else, it reads these docs.

Install it into your project with the [skills CLI](https://github.com/vercel-labs/skills):

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx skills add ruiyuwg/docscn
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx skills add ruiyuwg/docscn
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx skills add ruiyuwg/docscn
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x skills add ruiyuwg/docscn
    ```
  </CodeBlockTab>
</CodeBlockTabs>

The CLI asks which agents to install it for. The skill's source is in [`skills/docscn`](https://github.com/ruiyuwg/docscn/tree/main/skills/docscn).

## shadcn/ui's MCP server [#shadcnuis-mcp-server]

shadcn/ui's [MCP server](https://ui.shadcn.com/docs/mcp) lets agents browse, search and install registry items, including docscn's. The CLI finds `@docscn` items through the registry index, but the MCP server only searches registries listed in `components.json`, so add docscn's:

```json title="components.json"
{
  "registries": {
    "@docscn": "https://docscn.dev/r/{name}.json"
  }
}
```

Then set up the server for your agent (`claude`, `cursor`, `vscode`, `codex` or `opencode`):

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest mcp init --client claude
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest mcp init --client claude
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest mcp init --client claude
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest mcp init --client claude
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Agents can then answer requests like "show me docscn's sidebar components" or "add docscn's docs block".

## The docs as Markdown [#the-docs-as-markdown]

These docs are also available as Markdown, for agents and LLMs to read:

| URL                                                  | Content                                                                                        |
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| [`/llms.txt`](https://docscn.dev/llms.txt)           | An index of every page, with its description                                                   |
| [`/llms-full.txt`](https://docscn.dev/llms-full.txt) | Every page in one file                                                                         |
| `/docs/<page>.md`                                    | One page, for example [`/docs/getting-started.md`](https://docscn.dev/docs/getting-started.md) |

A request for the homepage or a docs page that prefers Markdown (`Accept: text/markdown`) also gets the Markdown: `llms.txt` for the homepage. The skill is also published for the [Agent Skills discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc) at [`/.well-known/agent-skills/index.json`](https://docscn.dev/.well-known/agent-skills/index.json). The **Copy Markdown** button at the top of each page copies it, and the menu next to it opens the page in ChatGPT, Claude and other AI tools.

To add the same to your own docs, see [Page actions](/docs/components/page-actions) and Fumadocs' [LLM guide](https://fumadocs.dev/docs/integrations/llms). To let your readers ask questions about your docs, add the [AI chat](/docs/components/ai-chat).

# Compatibility (/docs/compatibility)

> The status of every Fumadocs UI module in docscn.

This table lists every module that Fumadocs UI ([`@fumadocs/base-ui`](https://github.com/fuma-nama/fumadocs/tree/fumadocs@16.16.2/packages/base-ui) 16.16.2) exports, and its docscn equivalent at `@/components/docs/…`. Supported modules keep Fumadocs UI's exports and main props, apart from the differences listed in [Migrating from Fumadocs UI](/docs/migrating-from-fumadocs-ui#whats-different).

| `fumadocs-ui/…`                                | Status        | Notes                                                                                                       |
| ---------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------- |
| `components/accordion`                         | ✅ Supported   | On shadcn/ui `accordion`                                                                                    |
| `components/banner`                            | ✅ Supported   | Sets `--docs-banner-height` for docscn's layouts                                                            |
| `components/callout`                           | ✅ Supported   | `Callout` on shadcn/ui `alert`                                                                              |
| `components/card`                              | ✅ Supported   | `Cards` / `Card` on shadcn/ui `card`                                                                        |
| `components/codeblock`                         | ✅ Supported   | Code tabs on shadcn/ui `tabs`                                                                               |
| `components/codeblock.rsc`                     | ✅ Supported   | `ServerCodeBlock`                                                                                           |
| `components/dialog/search`                     | ✅ Supported   | On Base UI `Dialog`                                                                                         |
| `components/dialog/search-algolia`             | ➖ Not planned | Build one from the dialog parts                                                                             |
| `components/dialog/search-default`             | ✅ Supported   |                                                                                                             |
| `components/dialog/search-orama`               | ➖ Not planned | Build one from the dialog parts                                                                             |
| `components/dynamic-codeblock`                 | ✅ Supported   |                                                                                                             |
| `components/dynamic-codeblock.core`            | ✅ Supported   |                                                                                                             |
| `components/files`                             | ✅ Supported   | On shadcn/ui `collapsible`; renders `remark-mdx-files` output                                               |
| `components/github-info`                       | ✅ Supported   |                                                                                                             |
| `components/heading`                           | ✅ Supported   |                                                                                                             |
| `components/image-zoom`                        | ✅ Supported   | Ships react-medium-image-zoom's styles                                                                      |
| `components/inline-toc`                        | ✅ Supported   | On shadcn/ui `collapsible`                                                                                  |
| `components/sidebar/base`                      | ➖ Not planned | Replaced by the shadcn/ui `sidebar`; docscn's file only holds translated `SidebarTrigger` and `SidebarRail` |
| `components/sidebar/link-item`                 | ✅ Supported   | Exports `SidebarLinkItem` instead of `createLinkItemRenderer`                                               |
| `components/sidebar/page-tree`                 | ✅ Supported   | Exports `SidebarPageTree` instead of `createPageTreeRenderer`                                               |
| `components/sidebar/tabs`                      | ✅ Supported   |                                                                                                             |
| `components/sidebar/tabs/dropdown`             | ✅ Supported   | On shadcn/ui `dropdown-menu`                                                                                |
| `components/steps`                             | ✅ Supported   | Ships the `fd-steps` / `fd-step` styles for `remark-steps`                                                  |
| `components/tabs`                              | ✅ Supported   | On shadcn/ui `tabs`                                                                                         |
| `components/toc`                               | ✅ Supported   |                                                                                                             |
| `components/toc/block`                         | ✅ Supported   |                                                                                                             |
| `components/toc/clerk`                         | ✅ Supported   |                                                                                                             |
| `components/toc/default`                       | ✅ Supported   |                                                                                                             |
| `components/type-table`                        | ✅ Supported   | On shadcn/ui `collapsible`                                                                                  |
| `components/ui/*`                              | ➖ Not planned | Use your shadcn/ui primitives                                                                               |
| `contexts/i18n`                                | ✅ Supported   |                                                                                                             |
| `contexts/search`                              | ✅ Supported   |                                                                                                             |
| `contexts/tree`                                | ✅ Supported   |                                                                                                             |
| `i18n`                                         | ✅ Supported   | Translation keys match Fumadocs UI's, so its language packs work                                            |
| `layouts/docs`                                 | ✅ Supported   | On the shadcn/ui `sidebar`; no `slots`                                                                      |
| `layouts/docs/page`                            | ✅ Supported   | No `slots`                                                                                                  |
| `layouts/docs/page/slots/*`                    | ✅ Supported   | `breadcrumb`, `container`, `footer`, `toc`                                                                  |
| `layouts/docs/slots/*`                         | ✅ Supported   | `container`, `header`, `sidebar`                                                                            |
| `layouts/flux/*`                               | ➖ Not planned | Unless users ask                                                                                            |
| `layouts/glass/*`                              | ➖ Not planned | Unless users ask                                                                                            |
| `layouts/home`                                 | ✅ Supported   |                                                                                                             |
| `layouts/home/navbar`                          | ✅ Supported   |                                                                                                             |
| `layouts/home/not-found`                       | ✅ Supported   |                                                                                                             |
| `layouts/home/slots/*`                         | ✅ Supported   | `container`, `header`                                                                                       |
| `layouts/notebook`                             | ✅ Supported   | On the shadcn/ui `sidebar`, at full width; no `slots`                                                       |
| `layouts/notebook/page`                        | ✅ Supported   | No `slots`                                                                                                  |
| `layouts/notebook/page/slots/*`                | ✅ Supported   | `breadcrumb`, `container`, `footer`, `toc`                                                                  |
| `layouts/notebook/slots/*`                     | ✅ Supported   | `container`, `header`, `sidebar`                                                                            |
| `layouts/shared`                               | ✅ Supported   | No `slots`                                                                                                  |
| `layouts/shared/slots/language-select`         | ✅ Supported   | On shadcn/ui `popover`                                                                                      |
| `layouts/shared/slots/search-trigger`          | ✅ Supported   |                                                                                                             |
| `layouts/shared/slots/theme-switch`            | ✅ Supported   |                                                                                                             |
| `layouts/spacious/*`                           | ➖ Not planned | Unless users ask                                                                                            |
| `mdx`                                          | ✅ Supported   | `defaultMdxComponents` and `createRelativeLink` (server components only)                                    |
| `og`                                           | ✅ Supported   |                                                                                                             |
| `og/takumi`                                    | ➖ Not planned |                                                                                                             |
| `page`                                         | ➖ Not planned | The older `DocsPage` wrapper; use `layouts/docs/page`                                                       |
| `provider/next`                                | ✅ Supported   |                                                                                                             |
| `provider/base`                                | ✅ Supported   |                                                                                                             |
| `provider/astro, react-router, tanstack, waku` | ➖ Not planned | Next.js only                                                                                                |
| `utils/use-copy-button`                        | ✅ Supported   |                                                                                                             |
| `utils/use-footer-items`                       | ✅ Supported   |                                                                                                             |
| `utils/use-is-scroll-top`                      | ✅ Supported   |                                                                                                             |
| `css/*, style.css`                             | ➖ Not planned | Styles come from your shadcn/ui theme                                                                       |

Fumadocs' AI chat UI (`@fumadocs/ai-chat`, which `fumadocs add ai` installs into `components/ai/chat/`) is [`@docscn/ai-chat`](/docs/components/ai-chat), installed at the same path.

# Accordion (/docs/components/accordion)

> Collapsible sections that can be linked to.

<Accordions>
  <Accordion title="What is docscn?" id="what-is-docscn">
    A shadcn/ui registry of documentation components, built on Fumadocs Core.
  </Accordion>

  <Accordion title="Do I need Fumadocs UI?">
    No. docscn replaces it, and only needs Fumadocs Core and Fumadocs MDX.
  </Accordion>
</Accordions>

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/docs-accordion
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/docs-accordion
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/docs-accordion
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/docs-accordion
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```mdx
import { Accordion, Accordions } from "@/components/docs/components/accordion";

<Accordions>
  <Accordion title="What is docscn?" id="what-is-docscn">
    A shadcn/ui registry of documentation components.
  </Accordion>
  <Accordion title="Do I need Fumadocs UI?">No.</Accordion>
</Accordions>
```

The `docs` block adds `Accordions` and `Accordion` to your MDX components.

An `Accordion` with an `id` can be linked to: a link to `#id` opens it, and it shows a button that copies the link. Closed content stays searchable with the browser's find-in-page, which opens the matching item.

## Props [#props]

| Component    | Prop    | Type        | Description                                                         |
| ------------ | ------- | ----------- | ------------------------------------------------------------------- |
| `Accordion`  | `title` | `ReactNode` | The item's title.                                                   |
| `Accordion`  | `id`    | `string`    | Makes the item linkable, with a copy-link button.                   |
| `Accordion`  | `value` | `string`    | The item's value. Defaults to the title.                            |
| `Accordions` | ...     |             | shadcn/ui `accordion` props, such as `defaultValue` and `multiple`. |

Built on shadcn/ui `accordion`.

# AI chat (/docs/components/ai-chat)

> Let readers ask questions about your docs, answered from your pages.

<TryAIChat />

The demo above has no backend, so it plays a canned answer. Press <kbd>⌘</kbd> <kbd>/</kbd> to open the chat and <kbd>Esc</kbd> to close it.

## Installation [#installation]

The `ai-chat-openrouter` block adds the chat to your docs layout, with a chat route that searches your pages and answers through [OpenRouter](https://openrouter.ai):

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/ai-chat-openrouter
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/ai-chat-openrouter
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/ai-chat-openrouter
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/ai-chat-openrouter
    ```
  </CodeBlockTab>
</CodeBlockTabs>

It installs:

* `components/ai/chat/`: the chat UI (`@docscn/ai-chat`), where Fumadocs' CLI puts it
* `components/ai/search.tsx`: the chat state, from the AI SDK's `useChat`
* `components/ai/layout.tsx`: a client `DocsLayout` that passes the chat to `aiChat` and adds a floating **Ask AI** button
* `app/api/chat/route.ts`: the chat route

Then finish the setup:

1. Import `DocsLayout` from the client layout in `app/docs/layout.tsx`:

   ```tsx title="app/docs/layout.tsx"
   import { DocsLayout } from "@/components/docs/layouts/docs"; // [!code --]
   import { DocsLayout } from "@/components/ai/layout"; // [!code ++]
   ```

   For the [notebook layout](/docs/components/notebook-layout), change the import in `components/ai/layout.tsx` to `@/components/docs/layouts/notebook`.

2. Set your OpenRouter API key in `.env.local`. `OPENROUTER_MODEL` picks the model, `anthropic/claude-sonnet-5.5` by default:

   ```bash title=".env.local"
   OPENROUTER_API_KEY=...
   ```

The route searches each page's processed Markdown, so `lib/source.ts` needs `includeProcessedMarkdown`. The [`docs` block](/docs/getting-started) and `create-fumadocs-app` both turn it on:

```ts title="lib/source.ts"
const docs = defineDocs({
  dir: "content/docs",
  docs: {
    postprocess: {
      includeProcessedMarkdown: true,
    },
  },
});
```

To use another model provider, edit `app/api/chat/route.ts`. It's an [AI SDK](https://ai-sdk.dev) route, so any AI SDK provider works.

## Usage [#usage]

The chat UI is in `components/ai/chat`, and works with any AI SDK chat. `AIChatProvider` holds the open state and the chat, from `useChat`:

```tsx title="components/ai/search.tsx"
"use client";

import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import { AIChatProvider } from "./chat";

export function AIChat({ children }: { children: React.ReactNode }) {
  const chat = useChat({
    transport: new DefaultChatTransport({ api: "/api/chat" }),
  });

  return <AIChatProvider chat={chat}>{children}</AIChatProvider>;
}
```

Inside it, `useAIChat()` returns `{ open, setOpen }`. Pass `<AIChatPanel />` to the layout's [`aiChat`](/docs/components/docs-layout#ai-chat) option, as `components/ai/layout.tsx` does, and add `<AIChatTrigger />` for the floating button.

Each question is sent with the page it was asked from (`location` and `title`), as a `data-client` part. The chat route passes it to the model, so readers can ask about "this page".

### Parts [#parts]

The panel is built from parts you can arrange yourself:

* `AIChatHeader`: the chat's title, and buttons for a new chat and to close it
* `AIChatMessages`: the conversation. A new chat shows a description and suggested questions
* `AIChatInput`: the question form. Its draft is kept in `localStorage`
* `AIChatSearch`: a call of the route's `search` tool, which expands to the pages it found
* `AIChatSources`: a list of links to sources

## Props [#props]

### `AIChatProvider` [#aichatprovider]

<TypeTable
  type="{
  chat: {
    description: (
      <>
        The chat, from the AI SDK's <code>{&#x22;useChat()&#x22;}</code>.
      </>
    ),
    type: &#x22;UseChatHelpers<Message>&#x22;,
    required: true,
  },
  toMessage: {
    description: (
      <>
        The message to send for a question. By default, its text with the page
        it was asked from.
      </>
    ),
    type: '(text: string) => Parameters<UseChatHelpers<Message>[&#x22;sendMessage&#x22;]>[0]',
  },
  renderPart: {
    description: (
      <>
        Renders a message part other than text, such as a tool call. Keep it
        stable, so settled messages skip re-rendering.
      </>
    ),
    type: &#x22;(part, live: boolean) => ReactNode&#x22;,
  },
  description: {
    description: <>Shown under the title of a new chat.</>,
    type: &#x22;ReactNode&#x22;,
  },
  suggestions: {
    description: <>Questions suggested in a new chat.</>,
    type: &#x22;string[]&#x22;,
  },
}"
/>

## Translations [#translations]

The chat's strings use their own keys, as in Fumadocs' `@fumadocs/ai-chat`. Add them to your translations with `aiChatTranslations()`:

```ts title="lib/i18n.ts"
import { aiChatTranslations } from "@/components/ai/chat/i18n";
import { uiTranslations } from "@/components/docs/i18n";

export const translations = i18n
  .translations()
  .extend(uiTranslations())
  .extend(aiChatTranslations());
```

See [Internationalization](/docs/internationalization).

## Migrating from Fumadocs [#migrating-from-fumadocs]

A Fumadocs project that ran `fumadocs add ai` has the chat UI in `components/ai/chat/`, importing from `fumadocs-ui`. Install docscn's over it:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/ai-chat --overwrite
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/ai-chat --overwrite
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/ai-chat --overwrite
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/ai-chat --overwrite
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Your `components/ai/search.tsx`, `components/ai/layout.tsx` and chat route keep working: replacing `fumadocs-ui/` with `@/components/docs/` in your imports also updates the layout's.

# Banner (/docs/components/banner)

> An announcement bar above the layout, optionally dismissible.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/banner
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/banner
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/banner
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/banner
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

Put it above the layout, for example in `app/docs/layout.tsx`:

```tsx title="app/docs/layout.tsx"
import { Banner } from "@/components/docs/components/banner";

export default function Layout({ children }: LayoutProps<"/docs">) {
  return (
    <>
      <Banner id="v1">docscn v1 is out</Banner>
      <DocsLayout tree={source.getPageTree()} {...baseOptions()}>
        {children}
      </DocsLayout>
    </>
  );
}
```

The banner sticks to the top of the page, and moves docscn's sidebar, navbars and table of contents down by its height. It does this by setting the `--docs-banner-height` variable, which you can use in your own sticky elements too: `top-(--docs-banner-height,0px)`.

With an `id`, the banner gets a close button and stays closed on later visits. Change the `id` to show a new announcement.

`variant="rainbow"` shows an animated gradient behind the text.

## Props [#props]

<TypeTable
  type="{
  id: {
    description: (
      <>
        Makes the banner dismissible, and remembers that in{&#x22; &#x22;}
        <code>{&#x22;localStorage&#x22;}</code>.
      </>
    ),
    type: &#x22;string&#x22;,
  },
  variant: {
    type: '&#x22;normal&#x22; | &#x22;rainbow&#x22;',
    default: '&#x22;normal&#x22;',
  },
  rainbowColors: {
    description: (
      <>
        The gradient's colours, with <code>{'variant=&#x22;rainbow&#x22;'}</code>.
      </>
    ),
    type: &#x22;string[]&#x22;,
  },
  height: {
    type: &#x22;string&#x22;,
    default: '&#x22;3rem&#x22;',
  },
  changeLayout: {
    description: <>Move docscn's layouts down by the banner's height.</>,
    type: &#x22;boolean&#x22;,
    default: &#x22;true&#x22;,
  },
}"
/>

# Callout (/docs/components/callout)

> Highlight notes, tips and warnings, built on the shadcn/ui alert.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/callout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/callout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/callout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/callout
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```mdx
<Callout title="Note">The default type is info.</Callout>

<Callout type="warn" title="Warning">
  Be careful with this.
</Callout>
```

<Callout title="Note">
  The default type is info.
</Callout>

<Callout type="warn" title="Warning">
  Be careful with this.
</Callout>

<Callout type="error" title="Error">
  Something went wrong.
</Callout>

<Callout type="success" title="Success">
  It worked.
</Callout>

<Callout type="idea" title="Idea">
  Try this.
</Callout>

## Props [#props]

<TypeTable
  type="{
  type: {
    description: (
      <>
        Sets the colour and icon. <code>{&#x22;warn&#x22;}</code> and{&#x22; &#x22;}
        <code>{&#x22;warning&#x22;}</code> are the same.
      </>
    ),
    type: '&#x22;info&#x22; | &#x22;warn&#x22; | &#x22;warning&#x22; | &#x22;error&#x22; | &#x22;success&#x22; | &#x22;idea&#x22;',
    default: '&#x22;info&#x22;',
  },
  title: {
    description: <>An optional title.</>,
    type: &#x22;ReactNode&#x22;,
  },
  icon: {
    description: <>Replace the type's icon.</>,
    type: &#x22;ReactNode&#x22;,
  },
}"
/>

`Callout` is built on shadcn/ui `alert`. `CalloutContainer`, `CalloutTitle` and `CalloutDescription` are exported to compose your own.

## Admonitions [#admonitions]

Fumadocs' opt-in admonition plugins render with `Callout` too: `remark-admonition` turns blocks such as `:::warn` into a `Callout`, and `remark-directive-admonition` turns `:::note` directives into `CalloutContainer`, `CalloutTitle` and `CalloutDescription`. Their `note`, `tip`, `warning`, `danger` and `success` types map onto the types above.

# Card (/docs/components/card)

> Link cards with an icon, title and description.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/card
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/card
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/card
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/card
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```mdx
<Cards>
  <Card
    title="Getting started"
    href="/docs/getting-started"
    description="Add docs to your project."
  />
  <Card
    title="Fumadocs"
    href="https://fumadocs.dev"
    description="The framework docscn builds on."
  />
</Cards>
```

<Cards>
  <Card title="Getting started" href="/docs/getting-started" description="Add docs to your project." />

  <Card title="Fumadocs" href="https://fumadocs.dev" description="The framework docscn builds on." />
</Cards>

## Props [#props]

<TypeTable
  type="{
  title: {
    description: <>The card's title.</>,
    type: &#x22;ReactNode&#x22;,
    required: true,
  },
  description: {
    description: <>Text below the title.</>,
    type: &#x22;ReactNode&#x22;,
  },
  icon: {
    description: <>An icon above the title.</>,
    type: &#x22;ReactNode&#x22;,
  },
  href: {
    description: <>Makes the card a link.</>,
    type: &#x22;string&#x22;,
  },
  external: {
    description: <>Open the link as an external link.</>,
    type: &#x22;boolean&#x22;,
  },
}"
/>

Children render below the description. `Cards` lays cards out in two columns, and one on narrow containers. `Card` is built on shadcn/ui `card`.

# Code block (/docs/components/codeblock)

> Code blocks and code tabs for Shiki output.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/codeblock
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/codeblock
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/codeblock
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/codeblock
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

`defaultMdxComponents` renders every code block in your MDX with `CodeBlock`. Fumadocs MDX's syntax works as usual:

````mdx
```ts title="greet.ts"
export function greet(name: string) {
  return `Hello, ${name}!`; // [!code highlight]
}
```
````

```ts title="greet.ts"
export function greet(name: string) {
  return `Hello, ${name}!`; // [!code highlight]
}
```

* `title="..."` adds a title bar, with an icon for the language.
* The copy button copies the code.
* Shiki's notations add highlighted (`[!code highlight]`), added and removed (`[!code ++]`, `[!code --]`), focused (`[!code focus]`) and highlighted-word (`[!code word:x]`) styles, and `lineNumbers` shows line numbers.

Code blocks with `tab="..."` and ` ```npm ` blocks become code tabs, on shadcn/ui `tabs`:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm install next-themes
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add next-themes
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add next-themes
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add next-themes
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Props [#props]

<TypeTable
  type="{
  title: {
    description: <>The title bar's text.</>,
    type: &#x22;ReactNode&#x22;,
  },
  icon: {
    description: (
      <>The title bar's icon. A string is treated as SVG markup.</>
    ),
    type: &#x22;ReactNode&#x22;,
  },
  allowCopy: {
    description: <>Show the copy button.</>,
    type: &#x22;boolean&#x22;,
    default: &#x22;true&#x22;,
  },
  keepBackground: {
    description: <>Keep the background colour of the Shiki theme.</>,
    type: &#x22;boolean&#x22;,
  },
  viewportProps: {
    description: <>Props for the scrolling area.</>,
    type: &#x22;HTMLAttributes&#x22;,
  },
  Actions: {
    description: <>Replace the area that holds the copy button.</>,
    type: &#x22;(props) => ReactNode&#x22;,
  },
}"
/>

`CodeBlockTabs` also accepts `groupId` and `persist`, to keep the same tab selected across code tabs and page loads.

The component's CSS (the light and dark Shiki themes and the notation styles) was added to your global stylesheet. Add `not-docs-codeblock` to an element to opt it out.

# DocsLayout (/docs/components/docs-layout)

> The docs layout with a sidebar, built on the shadcn/ui sidebar.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/docs-layout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/docs-layout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/docs-layout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/docs-layout
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```tsx title="app/docs/layout.tsx"
import { DocsLayout } from "@/components/docs/layouts/docs";
import { baseOptions } from "@/lib/layout.shared";
import { source } from "@/lib/source";

export default function Layout({ children }: LayoutProps<"/docs">) {
  return (
    <DocsLayout tree={source.getPageTree()} {...baseOptions()}>
      {children}
    </DocsLayout>
  );
}
```

`DocsLayout` renders the shadcn/ui `SidebarProvider`, a `Sidebar` with the page tree, and a `SidebarInset` for the page. On mobile, the sidebar opens as a sheet from a navbar with the title, search and a sidebar trigger. On desktop, the sidebar can be collapsed (<kbd>⌘</kbd> <kbd>B</kbd>), leaving a small panel to reopen it.

Because it uses the shadcn/ui sidebar, `useSidebar()` from `@/components/ui/sidebar` works anywhere inside it.

## Props [#props]

<TypeTable
  type="{
  tree: {
    description: (
      <>
        The page tree, usually <code>{&#x22;source.getPageTree()&#x22;}</code>.
      </>
    ),
    type: &#x22;PageTree.Root&#x22;,
    required: true,
  },
  nav: {
    description: (
      <>
        <code>{&#x22;title&#x22;}</code>, <code>{&#x22;url&#x22;}</code> (defaults to{&#x22; &#x22;}
        <code>{&#x22;/&#x22;}</code>), <code>{&#x22;enabled&#x22;}</code>,{&#x22; &#x22;}
        <code>{&#x22;children&#x22;}</code> (extra navbar content) and{&#x22; &#x22;}
        <code>{&#x22;transparentMode&#x22;}</code>.
      </>
    ),
    type: &#x22;NavOptions&#x22;,
  },
  links: {
    description: (
      <>
        Links shown in the sidebar. Icon links show in the sidebar footer.{&#x22; &#x22;}
        <code>{'on: &#x22;nav&#x22; | &#x22;menu&#x22; | &#x22;all&#x22;'}</code> restricts where a link
        shows.
      </>
    ),
    type: &#x22;LinkItemType[]&#x22;,
  },
  githubUrl: {
    description: <>Adds a GitHub icon link.</>,
    type: &#x22;string&#x22;,
  },
  sidebar: {
    description: <>See below.</>,
    type: &#x22;SidebarOptions&#x22;,
  },
  tabs: {
    description: (
      <>
        Layout tabs, by default one per root folder (
        <code>{'&#x22;root&#x22;: true'}</code> in <code>{&#x22;meta.json&#x22;}</code>).
      </>
    ),
    type: &#x22;LayoutTab[] | GetLayoutTabsOptions | false&#x22;,
  },
  tabMode: {
    description: (
      <>
        Show tabs as a dropdown in the sidebar (<code>{&#x22;auto&#x22;}</code>), or
        above the page on desktop (<code>{&#x22;top&#x22;}</code>).
      </>
    ),
    type: '&#x22;auto&#x22; | &#x22;top&#x22;',
  },
  themeSwitch: {
    description: (
      <>
        The theme switch in the sidebar footer.{&#x22; &#x22;}
        <code>{'mode: &#x22;light-dark-system&#x22;'}</code> adds a system option.
      </>
    ),
    type: &#x22;{ enabled?, mode? }&#x22;,
  },
  searchToggle: {
    description: (
      <>The search triggers, with props for the small and full variants.</>
    ),
    type: &#x22;{ enabled?, sm?, full? }&#x22;,
  },
  containerProps: {
    description: (
      <>
        Props for the <code>{&#x22;SidebarInset&#x22;}</code> that wraps the page.
      </>
    ),
    type: 'ComponentProps<&#x22;main&#x22;>',
  },
  aiChat: {
    description: (
      <>
        An AI chat panel and whether it's open. See{&#x22; &#x22;}
        <a href=&#x22;#ai-chat&#x22;>AI chat</a>.
      </>
    ),
    type: &#x22;{ open, onOpenChange, panel? }&#x22;,
  },
}"
/>

### `sidebar` [#sidebar]

<TypeTable
  type="{
  enabled: {
    description: <>Show the sidebar.</>,
    type: &#x22;boolean&#x22;,
    default: &#x22;true&#x22;,
  },
  banner: {
    description: <>Content above the page tree.</>,
    type: &#x22;ReactNode&#x22;,
  },
  footer: {
    description: <>Content in the sidebar footer.</>,
    type: &#x22;ReactNode&#x22;,
  },
  collapsible: {
    description: <>Allow collapsing the sidebar on desktop.</>,
    type: &#x22;boolean&#x22;,
    default: &#x22;true&#x22;,
  },
  defaultOpenLevel: {
    description: <>Open folders up to this depth by default.</>,
    type: &#x22;number&#x22;,
    default: &#x22;0&#x22;,
  },
  prefetch: {
    description: <>Prefetch pages linked from the sidebar.</>,
    type: &#x22;boolean&#x22;,
  },
  defaultOpen: {
    description: (
      <>
        Whether the desktop sidebar starts open. Read shadcn/ui's{&#x22; &#x22;}
        <code>{&#x22;sidebar_state&#x22;}</code> cookie and pass it to keep the state
        across page loads.
      </>
    ),
    type: &#x22;boolean&#x22;,
  },
  open: {
    description: <>Control the desktop open state.</>,
    type: &#x22;boolean&#x22;,
  },
  onOpenChange: {
    description: <>Called when the desktop open state changes.</>,
    type: &#x22;(open: boolean) => void&#x22;,
  },
  components: {
    description: <>Replace how page tree nodes render.</>,
    type: &#x22;{ Item?, Folder?, Separator? }&#x22;,
  },
}"
/>

The rest are passed to the shadcn/ui `Sidebar` (e.g. `variant`, `side`, `className`).

## AI chat [#ai-chat]

Pass your chat as `aiChat.panel`, with its open state. While it's open, the layout docks the panel against the right edge of the window from the `xl` breakpoint, narrowing the page and hiding the table of contents. Below `xl`, it floats over the page. The panel renders the first time it opens.

The open state lives on the client, so render the layout from a client component:

```tsx title="components/ai/layout.tsx"
"use client";

import { useState } from "react";
import {
  DocsLayout as Layout,
  type DocsLayoutProps,
} from "@/components/docs/layouts/docs";
import { Chat } from "@/components/chat"; // your chat UI

export function DocsLayout(props: DocsLayoutProps) {
  const [open, setOpen] = useState(false);

  return (
    <Layout
      {...props}
      aiChat={{
        open,
        onOpenChange: setOpen,
        panel: <Chat onClose={() => setOpen(false)} />,
      }}
    />
  );
}
```

Then import `DocsLayout` from `@/components/ai/layout` in `app/docs/layout.tsx`. The layout has no button to open the chat, so add one to your chat UI, for example a floating button or a link in `nav.children`.

For a ready-made chat, install the [AI chat](/docs/components/ai-chat) block, which adds this layout with docscn's chat UI and a chat route.

# DocsPage (/docs/components/docs-page)

> A docs page with a table of contents, breadcrumb and previous/next links.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/docs-page
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/docs-page
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/docs-page
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/docs-page
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```tsx title="app/docs/[[...slug]]/page.tsx"
import { notFound } from "next/navigation";
import { getMDXComponents } from "@/components/mdx";
import {
  DocsBody,
  DocsDescription,
  DocsPage,
  DocsTitle,
} from "@/components/docs/layouts/docs/page";
import { createRelativeLink } from "@/components/docs/mdx";
import { source } from "@/lib/source";

export default async function Page(props: PageProps<"/docs/[[...slug]]">) {
  const params = await props.params;
  const page = source.getPage(params.slug);
  if (!page) notFound();

  const MDX = page.data.body;

  return (
    <DocsPage toc={page.data.toc} full={page.data.full}>
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsDescription>{page.data.description}</DocsDescription>
      <DocsBody>
        <MDX
          components={getMDXComponents({ a: createRelativeLink(source, page) })}
        />
      </DocsBody>
    </DocsPage>
  );
}
```

Use it inside [`DocsLayout`](/docs/components/docs-layout). `DocsBody` adds the [`docs-typeset`](/docs/theming#typography) class.

## Props [#props]

<TypeTable
  type="{
  toc: {
    description: (
      <>
        The page's headings, usually <code>{&#x22;page.data.toc&#x22;}</code>.
      </>
    ),
    type: &#x22;TOCItemType[]&#x22;,
  },
  full: {
    description: <>Use the full width and hide the desktop TOC.</>,
    type: &#x22;boolean&#x22;,
  },
  tableOfContent: {
    description: (
      <>
        The TOC beside the page on wide screens. <code>{&#x22;single: true&#x22;}</code>{&#x22; &#x22;}
        highlights one heading at a time, and <code>{&#x22;style&#x22;}</code> picks one
        of the <a href=&#x22;/docs/components/toc#styles&#x22;>TOC styles</a>.
      </>
    ),
    type: &#x22;{ enabled?, single?, style?, header?, footer?, container?, list? }&#x22;,
  },
  tableOfContentPopover: {
    description: (
      <>
        The TOC popover at the top of the page on smaller screens, with the
        same <code>{&#x22;style&#x22;}</code> option.
      </>
    ),
    type: &#x22;{ enabled?, style?, header?, footer?, ... }&#x22;,
  },
  breadcrumb: {
    description: (
      <>
        The breadcrumb above the page, on shadcn/ui{&#x22; &#x22;}
        <code>{&#x22;breadcrumb&#x22;}</code>.
      </>
    ),
    type: &#x22;{ enabled?, includeRoot?, includePage?, includeSeparator? }&#x22;,
  },
  footer: {
    description: (
      <>
        Previous and next page links, taken from the page tree unless you pass{&#x22; &#x22;}
        <code>{&#x22;items&#x22;}</code>.
      </>
    ),
    type: &#x22;{ enabled?, items? }&#x22;,
  },
}"
/>

The rest are passed to the page's `<article>`.

## Other exports [#other-exports]

* `EditOnGitHub`: a link button, e.g. `<EditOnGitHub href="https://github.com/…/edit/main/content/docs/index.mdx" />`.
* `PageLastUpdate`: shows `Last updated on <date>` in the reader's locale, from a `date` prop.
* `PageBreadcrumb` and `PageFooter`: the breadcrumb and footer on their own.
* `MarkdownCopyButton` and `ViewOptionsPopover`: the [page actions](/docs/components/page-actions).
* `useDocsPage()`: returns `{ full }`.

# Sidebar (/docs/components/docs-sidebar)

> Render a Fumadocs page tree with the shadcn/ui sidebar.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/docs-sidebar
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/docs-sidebar
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/docs-sidebar
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/docs-sidebar
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

[`DocsLayout`](/docs/components/docs-layout) renders the sidebar for you. Use `SidebarPageTree` directly to build your own layout on the shadcn/ui sidebar. It reads the page tree from `TreeContextProvider` ([tree context](/docs/components/utilities)):

```tsx
import {
  Sidebar,
  SidebarContent,
  SidebarProvider,
} from "@/components/ui/sidebar";
import { SidebarPageTree } from "@/components/docs/components/sidebar/page-tree";
import { TreeContextProvider } from "@/components/docs/contexts/tree";

<TreeContextProvider tree={tree}>
  <SidebarProvider>
    <Sidebar>
      <SidebarContent>
        <SidebarPageTree />
      </SidebarContent>
    </Sidebar>
  </SidebarProvider>
</TreeContextProvider>;
```

Top-level separators become `SidebarGroup` labels. Pages render as `SidebarMenuButton` links, and folders as collapsible `SidebarMenuSub` lists that open when the current page is inside them. A folder with an index page links to it and has a separate button to expand it. The active page is scrolled into view, and the mobile sheet closes after navigating.

`SidebarPageTree` accepts `Item`, `Folder` and `Separator` components to replace how each kind of node renders. Wrap it in `SidebarTreeOptionsProvider` to set `defaultOpenLevel` and `prefetch`.

`SidebarLinkItem` (in `components/sidebar/link-item`) renders a layout link (`LinkItemType`) the same way.

# Dynamic code block (/docs/components/dynamic-codeblock)

> A code block highlighted in the browser, for code that's only known at runtime.

<DynamicCodeBlock lang="ts" code="'const greeting = &#x22;Hello, world!&#x22;;'" />

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/dynamic-codeblock
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/dynamic-codeblock
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/dynamic-codeblock
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/dynamic-codeblock
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```tsx
import { DynamicCodeBlock } from "@/components/docs/components/dynamic-codeblock";

<DynamicCodeBlock lang="ts" code={code} />;
```

It loads Shiki in the browser and highlights the code with the GitHub light and dark themes, using the same [code block](/docs/components/codeblock) as your content. Until Shiki has loaded, it shows the code without colours.

Shiki's full bundle is large. To load less, use `DynamicCodeBlock` from `components/dynamic-codeblock.core` with your own highlighter, built with only the languages and themes you need.

For code that's known on the server, [`ServerCodeBlock`](/docs/components/server-codeblock) sends no JavaScript to the browser.

## Props [#props]

<TypeTable
  type="{
  lang: {
    description: <>The code's language.</>,
    type: &#x22;string&#x22;,
    required: true,
  },
  code: {
    type: &#x22;string&#x22;,
    required: true,
  },
  options: {
    description: (
      <>
        Shiki's options, such as <code>{&#x22;themes&#x22;}</code> and{&#x22; &#x22;}
        <code>{&#x22;components&#x22;}</code>.
      </>
    ),
    type: &#x22;UseShikiOptions&#x22;,
  },
  codeblock: {
    description: (
      <>
        Props for the underlying <code>{&#x22;CodeBlock&#x22;}</code>, such as{&#x22; &#x22;}
        <code>{&#x22;title&#x22;}</code>.
      </>
    ),
    type: &#x22;CodeBlockProps&#x22;,
  },
  wrapInSuspense: {
    description: <>Show the code without colours while Shiki loads.</>,
    type: &#x22;boolean&#x22;,
    default: &#x22;true&#x22;,
  },
}"
/>

# Files (/docs/components/files)

> A file tree with collapsible folders, for showing a project's structure in MDX.

<Files>
  <Folder name="app">
    <File name="layout.tsx" />

    <Folder name="docs">
      <File name="layout.tsx" />

      <File name="[[...slug]]/page.tsx" />
    </Folder>
  </Folder>

  <File name="package.json" />
</Files>

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/files
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/files
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/files
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/files
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```mdx
import { File, Files, Folder } from "@/components/docs/components/files";

<Files>
  <Folder name="app" defaultOpen>
    <File name="layout.tsx" />
  </Folder>
  <File name="package.json" />
</Files>
```

The `docs` block adds `Files`, `Folder` and `File` to your MDX components, which Fumadocs' opt-in `remark-mdx-files` plugin needs: it turns `files` code blocks into these components.

## Props [#props]

| Component | Prop          | Type        | Description                   |
| --------- | ------------- | ----------- | ----------------------------- |
| `Folder`  | `name`        | `string`    | The folder's name.            |
| `Folder`  | `defaultOpen` | `boolean`   | Open the folder at first.     |
| `Folder`  | `disabled`    | `boolean`   | Keep the folder from opening. |
| `File`    | `name`        | `string`    | The file's name.              |
| `File`    | `icon`        | `ReactNode` | Replace the file icon.        |

Folders are built on shadcn/ui `collapsible`.

# GitHub info (/docs/components/github-info)

> A link to a GitHub repository with its star and fork counts.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/github-info
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/github-info
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/github-info
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/github-info
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

`GithubInfo` is a React Server Component that fetches the counts from GitHub's API, cached for 60 seconds. It fits in the sidebar's footer or the layout's links:

```tsx title="lib/layout.shared.tsx"
import { GithubInfo } from "@/components/docs/components/github-info";

export function baseOptions(): BaseLayoutProps {
  return {
    links: [
      {
        type: "custom",
        children: <GithubInfo owner="ruiyuwg" repo="docscn" />,
      },
    ],
  };
}
```

Unauthenticated requests to GitHub's API are rate limited. Pass a `token` (for example `process.env.GITHUB_TOKEN`) on busy sites.

`fetchRepositoryInfo()` returns the counts without the link.

## Props [#props]

<TypeTable
  type="{
  owner: {
    type: &#x22;string&#x22;,
    required: true,
  },
  repo: {
    type: &#x22;string&#x22;,
    required: true,
  },
  token: {
    description: <>A GitHub token for the API request.</>,
    type: &#x22;string&#x22;,
  },
  baseUrl: {
    description: <>The API's URL, for GitHub Enterprise.</>,
    type: &#x22;string&#x22;,
    default: '&#x22;https://api.github.com&#x22;',
  },
  fetchOptions: {
    description: (
      <>
        Options for <code>{&#x22;fetch&#x22;}</code>.
      </>
    ),
    type: &#x22;RequestInit&#x22;,
    default: &#x22;{ next: { revalidate: 60 } }&#x22;,
  },
  locale: {
    description: <>The locale for the compact counts, such as 1.2K.</>,
    type: &#x22;Intl.LocalesArgument&#x22;,
  },
}"
/>

# Heading (/docs/components/heading)

> Headings with anchor links, so readers can link to any section of a page.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/heading
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/heading
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/heading
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/heading
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

`defaultMdxComponents` renders `h1`–`h6` with `Heading`. A heading with an `id` (Fumadocs MDX adds one to every heading) links to itself, and shows a button on hover that copies the link to the section.

```tsx
import { Heading } from "@/components/docs/components/heading";

<Heading as="h2" id="installation">
  Installation
</Heading>;
```

# HomeLayout (/docs/components/home-layout)

> A layout for landing pages, with a navbar that matches your docs.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/home-layout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/home-layout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/home-layout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/home-layout
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```tsx title="app/(home)/layout.tsx"
import { HomeLayout } from "@/components/docs/layouts/home";
import { baseOptions } from "@/lib/layout.shared";

export default function Layout({ children }: LayoutProps<"/">) {
  return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
}
```

The navbar shows the title, links (with navigation menus for `type: "menu"` links), search and the theme switch. On small screens, links move into a menu that opens below the navbar.

It takes the same `nav`, `links`, `githubUrl`, `themeSwitch` and `searchToggle` options as [`DocsLayout`](/docs/components/docs-layout), plus `nav.enableHoverToOpen` to open the mobile menu on hover. Other props are passed to its `<main>`.

## Not found page [#not-found-page]

`DefaultNotFound` is a ready-made 404 message with a link home:

```tsx title="app/not-found.tsx"
import { HomeLayout } from "@/components/docs/layouts/home";
import { DefaultNotFound } from "@/components/docs/layouts/home/not-found";
import { baseOptions } from "@/lib/layout.shared";

export default function NotFound() {
  return (
    <HomeLayout {...baseOptions()}>
      <DefaultNotFound />
    </HomeLayout>
  );
}
```

# Image zoom (/docs/components/image-zoom)

> An image that zooms in to fill the screen when clicked.

<ImageZoom src="gradient" alt="A gradient" />

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/image-zoom
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/image-zoom
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/image-zoom
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/image-zoom
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

`ImageZoom` takes the props of Next.js' `Image`. To make every image in your content zoomable, render Markdown images with it in `components/mdx.tsx`:

```tsx title="components/mdx.tsx"
import {
  ImageZoom,
  type ImageZoomProps,
} from "@/components/docs/components/image-zoom";

export function getMDXComponents(components?: MDXComponents) {
  return {
    ...defaultMdxComponents,
    img: (props) => <ImageZoom {...(props as ImageZoomProps)} />,
    ...components,
  } satisfies MDXComponents;
}
```

It's built on [react-medium-image-zoom](https://github.com/rpearce/react-medium-image-zoom). Installing it adds the styles the zoomed image needs to your global stylesheet, with the page's `--background` behind the image.

## Props [#props]

<TypeTable
  type="{
  zoomInProps: {
    description: (
      <>
        Props for the zoomed-in image, such as a higher-resolution{&#x22; &#x22;}
        <code>{&#x22;src&#x22;}</code>.
      </>
    ),
    type: 'ComponentProps<&#x22;img&#x22;>',
  },
  rmiz: {
    description: <>Props for react-medium-image-zoom.</>,
    type: &#x22;UncontrolledProps&#x22;,
  },
}"
/>

# Inline TOC (/docs/components/inline-toc)

> A collapsible table of contents to place inside a page.

<InlineTOC items="toc" />

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/inline-toc
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/inline-toc
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/inline-toc
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/inline-toc
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

In MDX, Fumadocs MDX passes the page's headings as `toc`:

```mdx
import { InlineTOC } from "@/components/docs/components/inline-toc";

<InlineTOC items={toc} />
```

Elsewhere, pass `page.data.toc`, or any `TOCItemType[]`. Children replace the "Table of Contents" label.

## Props [#props]

<TypeTable
  type="{
  items: {
    description: <>The headings to list.</>,
    type: &#x22;TOCItemType[]&#x22;,
    required: true,
  },
  defaultOpen: {
    description: (
      <>
        Start open. The other props of shadcn/ui <code>{&#x22;collapsible&#x22;}</code>{&#x22; &#x22;}
        work too.
      </>
    ),
    type: &#x22;boolean&#x22;,
    default: &#x22;false&#x22;,
  },
}"
/>

# Language select (/docs/components/language-select)

> Switch between the languages of your docs.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/language-select
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/language-select
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/language-select
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/language-select
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

When `RootProvider`'s `i18n` prop lists more than one language, the docs, notebook and home layouts show a language switcher: in the docs layout's sidebar, and in the navbar of the notebook and home layouts. See [Internationalization](/docs/internationalization) to set up the languages.

`LanguageSelect` is a button that opens a popover with the languages. Its children are the button's content, and `LanguageSelectText` shows the current language's name:

```tsx
import { Languages } from "lucide-react";
import {
  LanguageSelect,
  LanguageSelectText,
} from "@/components/docs/layouts/shared/slots/language-select";

<LanguageSelect>
  <Languages />
  <LanguageSelectText />
</LanguageSelect>;
```

Choosing a language calls `i18n.onLocaleChange` if you passed one to `RootProvider`. Otherwise it swaps the locale at the start of the URL, following your i18n config's `hideLocale` option.

## Props [#props]

<TypeTable
  type="{
  variant: {
    description: <>The shadcn/ui button variant.</>,
    type: &#x22;ButtonVariant&#x22;,
    default: '&#x22;ghost&#x22;',
  },
}"
/>

# MDX components (/docs/components/mdx)

> The components for Fumadocs MDX content: code blocks, headings, images, tables, cards and callouts.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/mdx
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/mdx
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/mdx
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/mdx
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

Spread `defaultMdxComponents` into your MDX components:

```tsx title="components/mdx.tsx"
import type { MDXComponents } from "mdx/types";
import defaultMdxComponents from "@/components/docs/mdx";

export function getMDXComponents(components?: MDXComponents) {
  return {
    ...defaultMdxComponents,
    ...components,
  } satisfies MDXComponents;
}
```

It covers everything Fumadocs MDX's default preset emits:

| Key                                                                          | Renders                                                      |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `pre`                                                                        | [`CodeBlock`](/docs/components/codeblock) for Shiki output   |
| `CodeBlockTabs`, `CodeBlockTabsList`, `CodeBlockTabsTrigger`, `CodeBlockTab` | Code tabs from `tab="..."` code blocks and ` ```npm ` blocks |
| `h1`–`h6`                                                                    | [`Heading`](/docs/components/heading) with anchor links      |
| `a`                                                                          | Fumadocs Core's `Link` (Next.js `Link` for internal links)   |
| `img`                                                                        | Next.js `Image`                                              |
| `table`                                                                      | A horizontally scrollable table                              |
| `Card`, `Cards`                                                              | [Cards](/docs/components/card)                               |
| `Callout`, `CalloutContainer`, `CalloutTitle`, `CalloutDescription`          | [Callouts](/docs/components/callout)                         |

The `docs` block's `components/mdx.tsx` also adds [Tabs](/docs/components/tabs), [Steps](/docs/components/steps), [Accordion](/docs/components/accordion), [Files](/docs/components/files) and [TypeTable](/docs/components/type-table), which Fumadocs UI's default components leave out. If you migrated from Fumadocs UI, import them in `components/mdx.tsx` the same way.

## Relative links [#relative-links]

`createRelativeLink` resolves links to other MDX files (`[Next](./next.mdx)`) to their page URLs. It works in server components only:

```tsx
<MDX components={getMDXComponents({ a: createRelativeLink(source, page) })} />
```

# Notebook layout (/docs/components/notebook-layout)

> A more compact docs layout, with a navbar beside or above the sidebar.

<div className="not-docs-typeset overflow-hidden rounded-xl border dark:hidden">
    <img alt="The notebook layout with the navbar above the sidebar and layout tabs below it" src="__img0" />
</div>

<div className="not-docs-typeset hidden overflow-hidden rounded-xl border dark:block">
    <img alt="The notebook layout with the navbar above the sidebar and layout tabs below it" src="__img1" />
</div>

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/notebook-layout @docscn/notebook-page
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/notebook-layout @docscn/notebook-page
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/notebook-layout @docscn/notebook-page
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/notebook-layout @docscn/notebook-page
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

The notebook layout has its own `DocsLayout` and `DocsPage`. Import both from `layouts/notebook`:

```tsx title="app/docs/layout.tsx"
import { DocsLayout } from "@/components/docs/layouts/notebook";
import { baseOptions } from "@/lib/layout.shared";
import { source } from "@/lib/source";

export default function Layout({ children }: LayoutProps<"/docs">) {
  const base = baseOptions();

  return (
    <DocsLayout
      {...base}
      tree={source.getPageTree()}
      nav={{ ...base.nav, mode: "top" }}
      tabMode="navbar"
    >
      {children}
    </DocsLayout>
  );
}
```

```tsx title="app/docs/[[...slug]]/page.tsx"
import {
  DocsBody,
  DocsDescription,
  DocsPage,
  DocsTitle,
} from "@/components/docs/layouts/notebook/page";
```

The page components take the same props as [`DocsPage`](/docs/components/docs-page) and its companions, so switching layouts means changing `layouts/docs` to `layouts/notebook` in both imports.

Like [`DocsLayout`](/docs/components/docs-layout), it's built on the shadcn/ui sidebar, so `useSidebar()` works inside it. Unlike it, the navbar shows on desktop too, with search, the links and the theme switch. When the sidebar is collapsed, the navbar shows the title and a button to open the sidebar again.

The layout spans the full width of the window, with the sidebar against its edge. Fumadocs UI's notebook layout is centred at up to 97rem wide.

The [AI chat](/docs/components/docs-layout#ai-chat) panel (`aiChat`) works as in `DocsLayout`. With `nav.mode: "top"`, it docks below the navbar.

## Props [#props]

It takes the same props as [`DocsLayout`](/docs/components/docs-layout), except for the ones below.

<TypeTable
  type="{
  nav: {
    description: (
      <>
        As for <code>{&#x22;DocsLayout&#x22;}</code>, plus <code>{&#x22;mode&#x22;}</code>:{&#x22; &#x22;}
        <code>{&#x22;top&#x22;}</code> places the navbar above the sidebar, across the
        whole width, and <code>{&#x22;auto&#x22;}</code> places it beside the sidebar.
      </>
    ),
    type: 'NavOptions & { mode?: &#x22;top&#x22; | &#x22;auto&#x22; }',
    default: '{ mode: &#x22;auto&#x22; }',
  },
  links: {
    description: (
      <>
        Links shown in the navbar from the <code>{&#x22;lg&#x22;}</code> breakpoint, and
        in the sidebar below it. <code>{'type: &#x22;menu&#x22;'}</code> links open a
        menu on hover.
      </>
    ),
    type: &#x22;LinkItemType[]&#x22;,
  },
  tabMode: {
    description: (
      <>
        Show layout tabs as a dropdown in the sidebar (
        <code>{&#x22;sidebar&#x22;}</code>), or as a row below the navbar from the{&#x22; &#x22;}
        <code>{&#x22;lg&#x22;}</code> breakpoint (<code>{&#x22;navbar&#x22;}</code>).
      </>
    ),
    type: '&#x22;sidebar&#x22; | &#x22;navbar&#x22;',
    default: '&#x22;sidebar&#x22;',
  },
  themeSwitch: {
    description: (
      <>The theme switch, in the navbar, or the sidebar on mobile.</>
    ),
    type: &#x22;{ enabled?, mode? }&#x22;,
  },
  containerProps: {
    description: (
      <>Props for the element that wraps the navbar, sidebar and page.</>
    ),
    type: 'ComponentProps<&#x22;div&#x22;>',
  },
}"
/>

`sidebar` takes the same options as `DocsLayout`'s, except `enabled`: the notebook layout always has a sidebar. Search shows in the navbar, not the sidebar.

# OG images (/docs/components/og)

> Generate Open Graph images for docs pages.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/og
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/og
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/og
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/og
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```tsx title="app/og/docs/[...slug]/route.tsx"
import { notFound } from "next/navigation";
import { generateOGImage } from "@/components/docs/og";
import { source } from "@/lib/source";

export async function GET(
  _req: Request,
  { params }: RouteContext<"/og/docs/[...slug]">,
) {
  const { slug } = await params;
  const page = source.getPage(slug.slice(0, -1));
  if (!page) notFound();

  return generateOGImage({
    title: page.data.title,
    description: page.data.description,
    site: "My App",
  });
}
```

`generateOGImage` returns a 1200×630 `ImageResponse` from `next/og`. It takes `title`, `description`, `site`, `icon`, `primaryColor` and `primaryTextColor`, plus `ImageResponse` options such as `fonts`. `generate` returns the image's JSX, to render with your own `ImageResponse`.

# Page actions (/docs/components/page-actions)

> Copy a page as Markdown, or open it in GitHub and AI tools.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/page-actions
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/page-actions
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/page-actions
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/page-actions
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

Both actions need a route that serves the page's Markdown. With Fumadocs MDX, enable `includeProcessedMarkdown` and add a route as in the [Fumadocs docs](https://fumadocs.dev/docs/integrations/llms). Then add the actions to your page:

```tsx title="app/docs/[[...slug]]/page.tsx"
import {
  MarkdownCopyButton,
  ViewOptionsPopover,
} from "@/components/docs/layouts/docs/page";

<div className="flex flex-row items-center gap-2 border-b pb-6">
  <MarkdownCopyButton markdownUrl={markdownUrl} />
  <ViewOptionsPopover
    markdownUrl={markdownUrl}
    githubUrl={`https://github.com/user/repo/blob/main/content/docs/${page.path}`}
  />
</div>;
```

| Component            | Props                                    | Description                                                                                                                     |
| -------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `MarkdownCopyButton` | `markdownUrl`                            | Copies the page's Markdown to the clipboard.                                                                                    |
| `ViewOptionsPopover` | `markdownUrl?`, `githubUrl?`, `pageUrl?` | A popover with links to the source on GitHub, the Markdown, and ChatGPT, Claude, Cursor and Scira with a prompt about the page. |

Both are built on shadcn/ui `button` and `popover`, and are also exported from `components/docs/layouts/shared/page-actions`.

# RootProvider (/docs/components/root-provider)

> Theme, search, translation and framework providers for the root layout.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/root-provider
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/root-provider
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/root-provider
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/root-provider
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

Wrap your app in `RootProvider` in the root layout, and add `suppressHydrationWarning` to `<html>` for next-themes:

```tsx title="app/layout.tsx"
import { RootProvider } from "@/components/docs/provider/next";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <RootProvider>{children}</RootProvider>
      </body>
    </html>
  );
}
```

It provides:

* [next-themes](https://github.com/pacocoursey/next-themes), with a <kbd>D</kbd> hotkey to switch between light and dark mode
* search, with the [default search dialog](/docs/components/search-dialog) opened by <kbd>⌘</kbd> <kbd>K</kbd> or <kbd>Ctrl</kbd> <kbd>K</kbd>
* shadcn/ui's `TooltipProvider`, which the sidebar uses
* Base UI's `DirectionProvider`
* translations of the UI strings, with the `i18n` prop
* Fumadocs Core's Next.js adapter, for links, images and routing

## Props [#props]

<TypeTable
  type="{
  theme: {
    description: (
      <>
        Options for next-themes. <code>{&#x22;enabled: false&#x22;}</code> removes it;{&#x22; &#x22;}
        <code>{&#x22;hotKey&#x22;}</code> changes or (<code>{&#x22;false&#x22;}</code>) disables
        the <kbd>D</kbd> hotkey.
      </>
    ),
    type: &#x22;ThemeProviderProps & { enabled?, hotKey? }&#x22;,
  },
  search: {
    description: (
      <>
        Search options. <code>{&#x22;SearchDialog&#x22;}</code> replaces the default
        dialog, and <code>{&#x22;options&#x22;}</code> passes props to it (e.g.{&#x22; &#x22;}
        <code>{'{ api: &#x22;/api/search&#x22; }'}</code>).
      </>
    ),
    type: &#x22;{ enabled?, SearchDialog?, options?, links?, hotKey? }&#x22;,
  },
  i18n: {
    description: (
      <>
        The UI strings' translations and the available languages. Build it
        with <code>{&#x22;i18nProvider()&#x22;}</code>; see{&#x22; &#x22;}
        <a href=&#x22;/docs/internationalization&#x22;>Internationalization</a>.
      </>
    ),
    type: &#x22;{ locale?, locales?, translations?, defaultLanguage?, hideLocale?, onLocaleChange? }&#x22;,
  },
  dir: {
    description: <>Text direction for Base UI components.</>,
    type: '&#x22;ltr&#x22; | &#x22;rtl&#x22;',
    default: '&#x22;ltr&#x22;',
  },
  components: {
    description: (
      <>
        Replace the Next.js <code>{&#x22;Link&#x22;}</code> and <code>{&#x22;Image&#x22;}</code>{&#x22; &#x22;}
        used by docscn components.
      </>
    ),
    type: &#x22;{ Link?, Image? }&#x22;,
  },
}"
/>

`useTheme` is re-exported from `@/components/docs/provider/base`.

# Search dialog (/docs/components/search-dialog)

> Search your docs from a dialog opened with a keyboard shortcut.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/search-dialog-default
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/search-dialog-default
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/search-dialog-default
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/search-dialog-default
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

`RootProvider` opens the default search dialog with <kbd>⌘</kbd> <kbd>K</kbd> (<kbd>Ctrl</kbd> <kbd>K</kbd> on Windows and Linux), or from the search triggers in the layouts. It queries `/api/search`, which you create with Fumadocs Core's search server:

```ts title="app/api/search/route.ts"
import { createFromSource } from "fumadocs-core/search/server";
import { source } from "@/lib/source";

export const { GET } = createFromSource(source);
```

Results show pages, headings and text with the matches highlighted. Use the arrow keys and <kbd>Enter</kbd> to open one, and <kbd>Esc</kbd> to close the dialog.

## Options [#options]

Pass options to the default dialog through `RootProvider`:

```tsx
<RootProvider
  search={{
    options: {
      api: "/api/search",
      delayMs: 100,
      tags: [{ name: "Guides", value: "guides" }],
    },
    links: [["Getting started", "/docs/getting-started"]],
  }}
>
```

| Option       | Type             | Description                                    |
| ------------ | ---------------- | ---------------------------------------------- |
| `api`        | `string`         | The search API URL. Defaults to `/api/search`. |
| `delayMs`    | `number`         | Debounce delay before searching.               |
| `tags`       | `TagItem[]`      | Tag filters shown below the results.           |
| `defaultTag` | `string`         | The tag selected at first.                     |
| `allowClear` | `boolean`        | Allow deselecting the tag.                     |
| `footer`     | `ReactNode`      | Extra content at the bottom of the dialog.     |
| `links`      | `[name, href][]` | Links shown before the reader types a query.   |

## Build your own dialog [#build-your-own-dialog]

The `search-dialog` component exports the parts the default dialog is made of, for other search clients:

```tsx
import { useDocsSearch } from "fumadocs-core/search/client";
import {
  SearchDialog,
  SearchDialogClose,
  SearchDialogContent,
  SearchDialogHeader,
  SearchDialogIcon,
  SearchDialogInput,
  SearchDialogList,
  SearchDialogOverlay,
  type SharedProps,
} from "@/components/docs/components/dialog/search";

export default function CustomSearchDialog(props: SharedProps) {
  const { search, setSearch, query } = useDocsSearch({ client: myClient });

  return (
    <SearchDialog
      search={search}
      onSearchChange={setSearch}
      isLoading={query.isLoading}
      {...props}
    >
      <SearchDialogOverlay />
      <SearchDialogContent>
        <SearchDialogHeader>
          <SearchDialogIcon />
          <SearchDialogInput />
          <SearchDialogClose />
        </SearchDialogHeader>
        <SearchDialogList items={query.data !== "empty" ? query.data : null} />
      </SearchDialogContent>
    </SearchDialog>
  );
}
```

Pass it to `RootProvider` as `search={{ SearchDialog: CustomSearchDialog }}`. The dialog is built on Base UI's `Dialog`, the primitive under shadcn/ui's `dialog`, with shadcn/ui `kbd`.

# Search trigger (/docs/components/search-trigger)

> Buttons that open the search dialog: a search box with its keyboard shortcut, or an icon.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/search-trigger
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/search-trigger
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/search-trigger
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/search-trigger
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

The layouts show these for you. To add one elsewhere inside `RootProvider`:

```tsx
import {
  FullSearchTrigger,
  SearchTrigger,
} from "@/components/docs/layouts/shared/slots/search-trigger";

<SearchTrigger />
<FullSearchTrigger className="w-60" />
```

`SearchTrigger` is an icon button, and accepts shadcn/ui `Button`'s `variant` and `size`. `FullSearchTrigger` looks like a search field and shows the hotkey with shadcn/ui `kbd`. Pass `hideIfDisabled` to hide either when search is turned off in `RootProvider`.

# Server code block (/docs/components/server-codeblock)

> A code block highlighted in a React Server Component.

<ServerCodeBlock lang="ts" code="'const greeting = &#x22;Hello, world!&#x22;;'" />

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/server-codeblock
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/server-codeblock
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/server-codeblock
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/server-codeblock
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```tsx
import { ServerCodeBlock } from "@/components/docs/components/codeblock.rsc";

<ServerCodeBlock lang="ts" code={code} />;
```

`ServerCodeBlock` is an async React Server Component. It highlights the code with Shiki on the server, using the same [code block](/docs/components/codeblock) as your content, and sends no highlighting code to the browser.

For code that's only known in the browser, use [`DynamicCodeBlock`](/docs/components/dynamic-codeblock).

## Props [#props]

<TypeTable
  type="{
  lang: {
    description: <>The code's language.</>,
    type: &#x22;string&#x22;,
    required: true,
  },
  code: {
    type: &#x22;string&#x22;,
    required: true,
  },
  themes: {
    description: (
      <>
        Shiki's light and dark themes. The other options of{&#x22; &#x22;}
        <code>{&#x22;highlight()&#x22;}</code> from{&#x22; &#x22;}
        <code>{&#x22;fumadocs-core/highlight&#x22;}</code> work too.
      </>
    ),
    type: &#x22;{ light: string; dark: string }&#x22;,
    default: '{ light: &#x22;github-light&#x22;, dark: &#x22;github-dark&#x22; }',
  },
  codeblock: {
    description: (
      <>
        Props for the underlying <code>{&#x22;CodeBlock&#x22;}</code>, such as{&#x22; &#x22;}
        <code>{&#x22;title&#x22;}</code>.
      </>
    ),
    type: &#x22;CodeBlockProps&#x22;,
  },
}"
/>

# Sidebar tabs (/docs/components/sidebar-tabs)

> Switch between root folders of the page tree.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/sidebar-tabs
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/sidebar-tabs
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/sidebar-tabs
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/sidebar-tabs
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

Mark folders as root folders in their `meta.json`:

```json title="content/docs/guides/meta.json"
{
  "title": "Guides",
  "description": "Learn the basics",
  "root": true
}
```

[`DocsLayout`](/docs/components/docs-layout) then shows a dropdown at the top of the sidebar to switch between them, built on shadcn/ui `dropdown-menu`. Only the current root folder's pages show in the sidebar.

Customise the tabs with the layout's `tabs` option: an array of `LayoutTab` (`title`, `url`, `icon`, `description`), `{ transform }` to change the generated tabs, or `false` to turn them off.

`getSidebarTabs(tree)` and `SidebarTabsDropdown` are exported for custom layouts.

# Steps (/docs/components/steps)

> Numbered steps for guides and tutorials, as components or with remark-steps.

<Steps>
  <Step>
    ### Install the registry [#install-the-registry]

    Add `@docscn` to your `components.json`.
  </Step>

  <Step>
    ### Add the components [#add-the-components]

    Install `@docscn/docs`.
  </Step>
</Steps>

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/steps
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/steps
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/steps
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/steps
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```mdx
import { Step, Steps } from "@/components/docs/components/steps";

<Steps>
  <Step>
    ### Install the registry

    Add `@docscn` to your `components.json`.

  </Step>
  <Step>
    ### Add the components

    Install `@docscn/docs`.

  </Step>
</Steps>
```

The `docs` block adds `Steps` and `Step` to your MDX components.

## remark-steps [#remark-steps]

Fumadocs' opt-in `remark-steps` plugin turns headings such as `### 1. Install` into steps, by wrapping them in `div`s with the `fd-steps` and `fd-step` classes. Installing `steps` adds the styles for those classes to your global stylesheet, so the plugin's output renders the same as the components.

# Tabs (/docs/components/tabs)

> Tabbed content, with selections shared between tab groups.

<Tabs items="[&#x22;pnpm&#x22;, &#x22;npm&#x22;, &#x22;yarn&#x22;]">
  <Tab>
    Install with 

    `pnpm add next-themes`

    .
  </Tab>

  <Tab>
    Install with 

    `npm install next-themes`

    .
  </Tab>

  <Tab>
    Install with 

    `yarn add next-themes`

    .
  </Tab>
</Tabs>

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/docs-tabs
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/docs-tabs
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/docs-tabs
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/docs-tabs
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

The `docs` block adds `Tabs` and `Tab` to your MDX components. Otherwise, add them to `components/mdx.tsx` or import them in a page:

```mdx
import { Tab, Tabs } from "@/components/docs/components/tabs";

<Tabs items={["pnpm", "npm", "yarn"]}>
  <Tab>Install with `pnpm add next-themes`.</Tab>
  <Tab>Install with `npm install next-themes`.</Tab>
  <Tab>Install with `yarn add next-themes`.</Tab>
</Tabs>
```

With `items`, each `Tab` matches an item by its order. Pass `value` to match a tab explicitly instead, or leave out `items` and compose the list yourself with `TabsList` and `TabsTrigger`:

```mdx
<Tabs defaultValue="react">
  <TabsList>
    <TabsTrigger value="react">React</TabsTrigger>
    <TabsTrigger value="vue">Vue</TabsTrigger>
  </TabsList>
  <TabsContent value="react">...</TabsContent>
  <TabsContent value="vue">...</TabsContent>
</Tabs>
```

## Shared and persisted selections [#shared-and-persisted-selections]

Tabs with the same `groupId` stay in sync: picking "npm" in one switches every tab group on the site with that `groupId`. The selection is kept for the session, and `persist` keeps it across visits.

```mdx
<Tabs groupId="package-manager" persist items={["pnpm", "npm", "yarn"]}>
```

## Linking to a tab [#linking-to-a-tab]

Give a `Tab` an `id`, and a link to `#id` opens that tab. A link to an element inside a tab opens the tab too. With `updateAnchor`, selecting a tab updates the URL hash to its `id`.

## Props [#props]

<TypeTable
  type="{
  items: {
    description: <>The tabs' labels, rendered as the tabs list.</>,
    type: &#x22;string[]&#x22;,
  },
  defaultIndex: {
    description: (
      <>
        The tab selected at first, with <code>{&#x22;items&#x22;}</code>.
      </>
    ),
    type: &#x22;number&#x22;,
    default: &#x22;0&#x22;,
  },
  label: {
    description: (
      <>
        Extra content at the start of the tabs list, with{&#x22; &#x22;}
        <code>{&#x22;items&#x22;}</code>.
      </>
    ),
    type: &#x22;ReactNode&#x22;,
  },
  groupId: {
    description: (
      <>
        Share the selection with other tabs that have the same{&#x22; &#x22;}
        <code>{&#x22;groupId&#x22;}</code>.
      </>
    ),
    type: &#x22;string&#x22;,
  },
  persist: {
    description: (
      <>
        Keep the selection in <code>{&#x22;localStorage&#x22;}</code> (with{&#x22; &#x22;}
        <code>{&#x22;groupId&#x22;}</code>).
      </>
    ),
    type: &#x22;boolean&#x22;,
  },
  updateAnchor: {
    description: (
      <>
        Update the URL hash to the selected tab's <code>{&#x22;id&#x22;}</code>.
      </>
    ),
    type: &#x22;boolean&#x22;,
  },
}"
/>

`Tabs` is built on shadcn/ui `tabs`, and also accepts its props, such as `defaultValue`, `value` and `onValueChange`.

# Theme switch (/docs/components/theme-switch)

> Switch between light, dark and system themes.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/theme-switch
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/theme-switch
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/theme-switch
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/theme-switch
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

The docs, notebook and home layouts show a theme switch. Configure it with the layout's `themeSwitch` option, or use it on its own inside `RootProvider`:

```tsx
import { ThemeSwitch } from "@/components/docs/layouts/shared/slots/theme-switch";

<ThemeSwitch />
<ThemeSwitch mode="light-dark-system" />
```

`mode="light-dark"` (the default) is a button that toggles between light and dark. `mode="light-dark-system"` is a shadcn/ui `toggle-group` with a system option.

# Table of contents (/docs/components/toc)

> The table of contents that follows the headings in view.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/toc
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/toc
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/toc
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/toc
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

[`DocsPage`](/docs/components/docs-page) shows the TOC beside the page on wide screens and as a popover on smaller ones. To use it elsewhere:

```tsx
import { TOCProvider, TOCScrollArea } from "@/components/docs/components/toc";
import { TOCItem, TOCItems } from "@/components/docs/components/toc/default";

<TOCProvider toc={toc}>
  <TOCScrollArea>
    <TOCItems>
      {toc.map((item) => (
        <TOCItem key={item.url} item={item} />
      ))}
    </TOCItems>
  </TOCScrollArea>
</TOCProvider>;
```

The active headings are highlighted with a thumb on a line that follows the heading levels. `useActiveAnchor`, `useActiveAnchors`, `useItems` and `useTOCItems` are re-exported from `components/toc`.

## Styles [#styles]

Pick a style for `DocsPage`'s TOC and TOC popover with `style`:

```tsx
<DocsPage
  toc={page.data.toc}
  tableOfContent={{ style: "clerk" }}
  tableOfContentPopover={{ style: "clerk" }}
>
  {/* ... */}
</DocsPage>
```

| Style              | Module                   | Looks like                                                                         |
| ------------------ | ------------------------ | ---------------------------------------------------------------------------------- |
| `normal` (default) | `components/toc/default` | A line that curves with the heading levels, and a thumb on the active headings.    |
| `clerk`            | `components/toc/clerk`   | A line that steps with the heading levels, highlighted beside the active headings. |
| `block`            | `components/toc/block`   | A highlight behind the active headings.                                            |

Each module exports `TOCItems`, `TOCItem` and `TOCEmpty`, so you can swap them in the example above.

# Type table (/docs/components/type-table)

> A table of props or fields that expand to show their details.

<TypeTable
  type="{
  title: {
    description: <>The card's title.</>,
    type: &#x22;ReactNode&#x22;,
    required: true,
  },
  href: {
    description: <>Makes the card a link.</>,
    type: &#x22;string&#x22;,
  },
  icon: {
    description: <>An icon shown above the title.</>,
    type: &#x22;ReactNode&#x22;,
    default: &#x22;undefined&#x22;,
  },
}"
/>

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/type-table
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/type-table
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/type-table
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/type-table
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Usage [#usage]

```mdx
import { TypeTable } from "@/components/docs/components/type-table";

<TypeTable
  type={{
    title: {
      description: <>The card's title.</>,
      type: "ReactNode",
      required: true,
    },
    href: {
      description: <>Makes the card a link.</>,
      type: "string",
    },
  }}
/>
```

The `docs` block adds `TypeTable` to your MDX components.

Give the table an `id` to make each field linkable: a field gets the id `{id}-{name}`, and opening it updates the URL hash.

## Props [#props]

<TypeTable
  type="{
  type: {
    description: <>The fields, by name.</>,
    type: &#x22;Record<string, TypeNode>&#x22;,
    required: true,
  },
  id: {
    description: <>Prefix for the fields' ids, which makes them linkable.</>,
    type: &#x22;string&#x22;,
  },
}"
/>

### `TypeNode` [#typenode]

<TypeTable
  type="{
  type: {
    description: <>The short type, shown in the row.</>,
    type: &#x22;ReactNode&#x22;,
    required: true,
  },
  description: {
    description: <>What the field does.</>,
    type: &#x22;ReactNode&#x22;,
  },
  typeDescription: {
    description: <>The full type, shown when the field is open.</>,
    type: &#x22;ReactNode&#x22;,
  },
  typeDescriptionLink: {
    description: <>Makes the short type a link.</>,
    type: &#x22;string&#x22;,
  },
  default: {
    description: <>The default value.</>,
    type: &#x22;ReactNode&#x22;,
  },
  required: {
    description: (
      <>
        Leaves the <code>{&#x22;?&#x22;}</code> off the field's name.
      </>
    ),
    type: &#x22;boolean&#x22;,
  },
  deprecated: {
    description: <>Strikes through the field's name.</>,
    type: &#x22;boolean&#x22;,
  },
  parameters: {
    description: <>For functions, each parameter's name and description.</>,
    type: &#x22;{ name: string; description: ReactNode }[]&#x22;,
  },
  returns: {
    description: <>For functions, what they return.</>,
    type: &#x22;ReactNode&#x22;,
  },
}"
/>

# Utilities (/docs/components/utilities)

> Building blocks the other components share.

These items are installed as dependencies of the components above. You rarely install them yourself, but they're useful for custom layouts and pages.

| Item             | Module                                    | Exports                                                                                                               |
| ---------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `layout-shared`  | `layouts/shared`                          | `BaseLayoutProps`, `NavOptions`, `LinkItemType`, `LayoutTab`, `getLayoutTabs`, `useLinkItems`, `LinkItem`, `NavTitle` |
| `tree-context`   | `contexts/tree`, `utils/use-footer-items` | `TreeContextProvider`, `useTreeContext`, `useTreePath`, `useTabsGroups`, `useFooterItems`                             |
| `search-context` | `contexts/search`                         | `SearchProvider`, `useSearchContext`, `SearchOnly`, `SharedProps`, `SearchLink`, `TagItem`                            |
| `docs-utils`     | `utils/*`                                 | `isActive`, `normalize`, `mergeRefs`, `useHotKey`, `useCopyButton`, `useIsScrollTop`                                  |
| `typography`     | Your global stylesheet                    | The [`docs-typeset`](/docs/theming#typography) class                                                                  |

All modules install under `components/docs/`, so `layouts/shared` is `@/components/docs/layouts/shared`.

# Getting started (/docs/getting-started)

> Add docs to a Next.js project that uses shadcn/ui, with one shadcn CLI command.

docscn supports Next.js projects that use shadcn/ui with [Base UI](https://base-ui.com) (the `base-*` styles).

## Create a project [#create-a-project]

If you don't have a project yet, create one with shadcn/ui and Base UI:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest init --template=next --base=base
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest init --template=next --base=base
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest init --template=next --base=base
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest init --template=next --base=base
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Install the docs block [#install-the-docs-block]

docscn is listed in the [shadcn/ui registry index](https://ui.shadcn.com/docs/registry/registry-index), so the CLI finds `@docscn` items without any setup. The `docs` block installs every docscn component, plus everything a docs site needs:

* `lib/source.ts`: loads `content/docs` with [Fumadocs MDX](https://fumadocs.dev/docs/mdx)
* `lib/layout.shared.tsx`: shared layout options (title, links, GitHub URL)
* `components/mdx.tsx`: the MDX components
* `app/docs/layout.tsx` and `app/docs/[[...slug]]/page.tsx`: the docs routes
* `app/api/search/route.ts`: the search API
* `content/docs/index.mdx` and `meta.json`: a first page

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/docs
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/docs
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/docs
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/docs
    ```
  </CodeBlockTab>
</CodeBlockTabs>

<Callout type="warn" title="pnpm 11">
  Fumadocs MDX depends on esbuild, and pnpm 11 won't install a package with an
  install script until you decide whether it may run. If the install stops with
  `ERR_PNPM_IGNORED_BUILDS`, pnpm has added a placeholder line, `esbuild: set
    this to true or false`, under `allowBuilds` in `pnpm-workspace.yaml`. Change
  it to `esbuild: false` (esbuild works without its script) and run the command
  again.
</Callout>

## Finish the setup [#finish-the-setup]

Two files need small edits the CLI can't make for you.

Wrap your app in `RootProvider`. It includes [next-themes](https://github.com/pacocoursey/next-themes), so it replaces the `ThemeProvider` from shadcn/ui's template or dark mode guide:

```tsx title="app/layout.tsx"
import { RootProvider } from "@/components/docs/provider/next"; // [!code ++]

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <RootProvider>{children}</RootProvider> {/* [!code ++] */}
      </body>
    </html>
  );
}
```

Compile MDX with Fumadocs MDX:

```ts title="next.config.ts"
import { createMDX } from "fumadocs-mdx/next"; // [!code ++]
import type { NextConfig } from "next";

const nextConfig: NextConfig = {};

export default createMDX()(nextConfig); // [!code ++]
```

## Run it [#run-it]

Start the dev server and open [localhost:3000/docs](http://localhost:3000/docs):

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm run dev
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm run dev
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dev
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun run dev
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Write pages as `.mdx` files in `content/docs`, and order them with `meta.json`. See the [Fumadocs docs](https://fumadocs.dev/docs) for the page and `meta.json` conventions.

## Install components one by one [#install-components-one-by-one]

You can also install components individually, for example:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/docs-layout @docscn/docs-page @docscn/mdx
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/docs-layout @docscn/docs-page @docscn/mdx
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/docs-layout @docscn/docs-page @docscn/mdx
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/docs-layout @docscn/docs-page @docscn/mdx
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Each component lists the shadcn/ui primitives and other docscn components it needs, and the CLI installs them too. See the component pages for what each one does.

## Monorepos [#monorepos]

In a [shadcn/ui monorepo](https://ui.shadcn.com/docs/monorepo), run the commands on this page in your app's directory (for example `apps/web`). docscn's components install into the app, and the shadcn/ui primitives they use into the shared `packages/ui`.

Two extra steps work around bugs in the shadcn CLI.

Before you install docscn, add the sidebar from `packages/ui`. Installed from the app, the CLI puts the sidebar's `use-mobile` hook in the app, where the sidebar can't import it ([shadcn-ui/ui#12212](https://github.com/shadcn-ui/ui/issues/12212)):

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add sidebar
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add sidebar
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add sidebar
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add sidebar
    ```
  </CodeBlockTab>
</CodeBlockTabs>

After you install docscn, add its dependencies to your app. The CLI adds them to `packages/ui/package.json` only, so the app can't import them ([shadcn-ui/ui#12213](https://github.com/shadcn-ui/ui/issues/12213)). Run these in your app's directory:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm install @base-ui/react @fuma-translate/react@^1.0.2 class-variance-authority cn fumadocs-core@^16.16.2 fumadocs-mdx@^15.4.6 lucide-react next-themes react-medium-image-zoom rehype-raw scroll-into-view-if-needed shiki@^4.5.0 unified unist-util-visit
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add @base-ui/react @fuma-translate/react@^1.0.2 class-variance-authority cn fumadocs-core@^16.16.2 fumadocs-mdx@^15.4.6 lucide-react next-themes react-medium-image-zoom rehype-raw scroll-into-view-if-needed shiki@^4.5.0 unified unist-util-visit
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add @base-ui/react @fuma-translate/react@^1.0.2 class-variance-authority cn fumadocs-core@^16.16.2 fumadocs-mdx@^15.4.6 lucide-react next-themes react-medium-image-zoom rehype-raw scroll-into-view-if-needed shiki@^4.5.0 unified unist-util-visit
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add @base-ui/react @fuma-translate/react@^1.0.2 class-variance-authority cn fumadocs-core@^16.16.2 fumadocs-mdx@^15.4.6 lucide-react next-themes react-medium-image-zoom rehype-raw scroll-into-view-if-needed shiki@^4.5.0 unified unist-util-visit
    ```
  </CodeBlockTab>
</CodeBlockTabs>

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm install -D @types/hast @types/mdx
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add -D @types/hast @types/mdx
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add --dev @types/hast @types/mdx
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add --dev @types/hast @types/mdx
    ```
  </CodeBlockTab>
</CodeBlockTabs>

These are the packages the `docs` block needs. The [AI chat](/docs/components/ai-chat) needs these too:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm install @ai-sdk/react@^4.0.0 ai@^7.0.0 @openrouter/ai-sdk-provider@^3.1.0 flexsearch@^0.8.212 hast-util-to-jsx-runtime@^2.3.6 remark@^15.0.1 remark-gfm@^4.0.1 remark-rehype@^11.1.2 remend@^1.4.0 zod@^4.0.0
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add @ai-sdk/react@^4.0.0 ai@^7.0.0 @openrouter/ai-sdk-provider@^3.1.0 flexsearch@^0.8.212 hast-util-to-jsx-runtime@^2.3.6 remark@^15.0.1 remark-gfm@^4.0.1 remark-rehype@^11.1.2 remend@^1.4.0 zod@^4.0.0
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add @ai-sdk/react@^4.0.0 ai@^7.0.0 @openrouter/ai-sdk-provider@^3.1.0 flexsearch@^0.8.212 hast-util-to-jsx-runtime@^2.3.6 remark@^15.0.1 remark-gfm@^4.0.1 remark-rehype@^11.1.2 remend@^1.4.0 zod@^4.0.0
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add @ai-sdk/react@^4.0.0 ai@^7.0.0 @openrouter/ai-sdk-provider@^3.1.0 flexsearch@^0.8.212 hast-util-to-jsx-runtime@^2.3.6 remark@^15.0.1 remark-gfm@^4.0.1 remark-rehype@^11.1.2 remend@^1.4.0 zod@^4.0.0
    ```
  </CodeBlockTab>
</CodeBlockTabs>

If you install components one by one, add the packages the CLI added to `packages/ui/package.json` instead. You can also delete the unused `hooks/use-mobile.ts` the CLI adds to your app.

# Introduction (/docs)

> Documentation components for Next.js and shadcn/ui, built on Fumadocs Core as a drop-in replacement for Fumadocs UI.

docscn is a [shadcn/ui](https://ui.shadcn.com) registry of documentation components: a docs layout with a sidebar, docs pages with a table of contents, search, and the MDX components for code blocks, callouts and cards. You install them into your own Next.js app with the shadcn CLI, and they're built on your shadcn/ui primitives and theme, so your docs look like the rest of your project.

## How it relates to Fumadocs [#how-it-relates-to-fumadocs]

docscn is a drop-in replacement for [Fumadocs UI](https://fumadocs.dev/docs/ui), built the shadcn/ui way.

* Content comes from [Fumadocs Core](https://fumadocs.dev/docs/headless) and [Fumadocs MDX](https://fumadocs.dev/docs/mdx), exactly as it does with Fumadocs UI. docscn components take Fumadocs Core's types (page trees, TOC items, search results), so any content source Fumadocs supports works.
* Components keep Fumadocs UI's names, props and module paths. `fumadocs-ui/layouts/docs` becomes `@/components/docs/layouts/docs`, so [migrating](/docs/migrating-from-fumadocs-ui) is a find-and-replace.
* Instead of a package with its own styles and colour themes, you get the source in `components/docs/`, built on your shadcn/ui `sidebar`, `button`, `tabs`, `alert` and other primitives, and styled with your theme tokens. Edit it like any other component in your app.

## What's included [#whats-included]

| Area       | Components                                                                                                                                                                                                                                                                                     |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Layouts    | [DocsLayout](/docs/components/docs-layout), [Notebook layout](/docs/components/notebook-layout), [HomeLayout](/docs/components/home-layout)                                                                                                                                                    |
| Pages      | [DocsPage](/docs/components/docs-page), [TOC](/docs/components/toc), [page actions](/docs/components/page-actions)                                                                                                                                                                             |
| Search     | [Search dialog](/docs/components/search-dialog)                                                                                                                                                                                                                                                |
| MDX        | [MDX components](/docs/components/mdx), [code block](/docs/components/codeblock), [callout](/docs/components/callout), [card](/docs/components/card), [tabs](/docs/components/tabs), [steps](/docs/components/steps), [accordion](/docs/components/accordion), [files](/docs/components/files) |
| Everything | [RootProvider](/docs/components/root-provider), [OG images](/docs/components/og)                                                                                                                                                                                                               |

See [Compatibility](/docs/compatibility) for the status of every Fumadocs UI module.

## Next steps [#next-steps]

<Cards>
  <Card title="Getting started" href="/docs/getting-started" description="Add docs to a new or existing shadcn/ui project." />

  <Card title="Migrating from Fumadocs UI" href="/docs/migrating-from-fumadocs-ui" description="Replace fumadocs-ui in an existing Fumadocs app." />
</Cards>

# Internationalization (/docs/internationalization)

> Translate docscn's UI strings, and add a language switcher.

docscn's components show their UI strings ("Search", "On this page", "Copy Markdown", ...) in English. To show them in other languages, pass translations to `RootProvider`. The API is Fumadocs UI's, so its [translation](https://fumadocs.dev/docs/ui/translations) and [internationalization](https://fumadocs.dev/docs/internationalization) guides apply, with imports from `@/components/docs/` instead of `fumadocs-ui/`.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/i18n
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/i18n
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/i18n
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/i18n
    ```
  </CodeBlockTab>
</CodeBlockTabs>

`@docscn/fumadocs-ui` already includes it. `RootProvider` and the layouts support translations without it; it adds the helpers below.

## One language [#one-language]

To translate the UI into a single language, define the translations with Fumadocs Core and pass them to `RootProvider`:

```tsx title="lib/layout.shared.tsx"
import { defineTranslations } from "fumadocs-core/i18n";
import { uiTranslations } from "@/components/docs/i18n";

export const translations = defineTranslations().extend(uiTranslations()).add({
  "Search(search trigger)": "搜索文档",
  "On this page(table of contents)": "本页目录",
});
```

```tsx title="app/layout.tsx"
import { i18nProvider } from "@/components/docs/i18n";
import { RootProvider } from "@/components/docs/provider/next";
import { translations } from "@/lib/layout.shared";

// ...
<RootProvider i18n={i18nProvider(translations)}>{children}</RootProvider>;
```

Each key is the English string, followed by notes in parentheses that say where it appears. `uiTranslations()` registers the keys, so TypeScript checks them; the full list is in `components/docs/.translations/index.ts`.

The [AI chat](/docs/components/ai-chat)'s strings have their own keys, in `components/ai/chat/.translations/index.ts`. Register them with `.extend(aiChatTranslations())` from `@/components/ai/chat/i18n`.

## Several languages [#several-languages]

For docs in several languages, follow Fumadocs' [Next.js internationalization guide](https://fumadocs.dev/docs/internationalization/next): it defines the languages in `lib/i18n.ts`, adds the locale middleware, moves your routes under `app/[lang]`, and passes `i18n` to `loader()`. Then add the translations for each language, and pass the current one to `RootProvider`:

```tsx title="lib/layout.shared.tsx"
import { uiTranslations } from "@/components/docs/i18n";
import { i18n } from "@/lib/i18n";

export const translations = i18n
  .translations()
  .extend(uiTranslations())
  .add({
    en: { displayName: "English" },
    cn: {
      displayName: "中文",
      "Search(search trigger)": "搜索文档",
    },
  });
```

```tsx title="app/[lang]/layout.tsx"
import { i18nProvider } from "@/components/docs/i18n";
import { RootProvider } from "@/components/docs/provider/next";
import { translations } from "@/lib/layout.shared";

export default async function Layout({
  params,
  children,
}: LayoutProps<"/[lang]">) {
  const { lang } = await params;

  return (
    <html lang={lang} suppressHydrationWarning>
      <body>
        <RootProvider i18n={i18nProvider(translations, lang)}>
          {children}
        </RootProvider>
      </body>
    </html>
  );
}
```

With more than one language, the docs, notebook and home layouts show a [language switcher](/docs/components/language-select). Choosing a language swaps the locale at the start of the URL, following the `hideLocale` option.

`defineI18nUI(i18n, translations)` from `@/components/docs/i18n` also works, as in Fumadocs UI: pass `i18nUI.provider(lang)` to `RootProvider`.

## Language packs [#language-packs]

Fumadocs' language packs ([`@fumadocs/language`](https://fumadocs.dev/docs/ui/translations#language-packs)) use the same keys, so they translate docscn too:

```ts
import { zhCN } from "@fumadocs/language/zh-cn";

export const translations = i18n
  .translations()
  .extend(uiTranslations())
  .preset("cn", zhCN());
```

docscn has two strings of its own, which language packs don't include: the labels of the sidebar's folder toggles, `Collapse(sidebar)(aria-label)` and `Expand(sidebar)(aria-label)`. Add them with `.add()`.

## Your own components [#your-own-components]

The components read their strings with `useTranslations()` from [`@fuma-translate/react`](https://www.npmjs.com/package/@fuma-translate/react), the package Fumadocs UI uses. Use it for strings you add to your copies:

```tsx
import { useTranslations } from "@fuma-translate/react";

const t = useTranslations({ note: "page footer" });
t("Report an issue"); // looks up "Report an issue(page footer)"
```

Register the new keys before translating them:

```ts
i18n
  .translations()
  .extend(uiTranslations())
  .extend({ keys: ["Report an issue(page footer)"] })
  .add({ cn: { "Report an issue(page footer)": "报告问题" } });
```

# Migrating from Fumadocs UI (/docs/migrating-from-fumadocs-ui)

> Replace fumadocs-ui in an existing Fumadocs app with docscn, mostly by changing import paths.

docscn keeps Fumadocs UI's component names, props and module paths, so a project made with `create-fumadocs-app` (Next.js and Fumadocs MDX) migrates in a few steps. docscn's CI runs exactly these steps on the stock template on every change.

## Steps [#steps]

### 1. Set up shadcn/ui [#1-set-up-shadcnui]

If the project doesn't use shadcn/ui yet, initialise it with Base UI. It detects `app/global.css`, adds the theme tokens and creates `components.json`:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest init --base=base
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest init --base=base
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest init --base=base
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest init --base=base
    ```
  </CodeBlockTab>
</CodeBlockTabs>

<Callout type="warn" title="pnpm 11">
  `create-fumadocs-app` leaves a placeholder for esbuild under `allowBuilds` in
  `pnpm-workspace.yaml`, and pnpm 11 refuses to install anything until you
  replace it. Set it to `esbuild: false` first.
</Callout>

### 2. Install the components [#2-install-the-components]

Install `@docscn/fumadocs-ui`, which installs every docscn component into `components/docs/` (but no routes):

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/fumadocs-ui
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/fumadocs-ui
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/fumadocs-ui
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/fumadocs-ui
    ```
  </CodeBlockTab>
</CodeBlockTabs>

If you added AI chat with `fumadocs add ai`, its UI in `components/ai/chat/` imports from `fumadocs-ui`. Replace it with docscn's:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest add @docscn/ai-chat --overwrite
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest add @docscn/ai-chat --overwrite
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest add @docscn/ai-chat --overwrite
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest add @docscn/ai-chat --overwrite
    ```
  </CodeBlockTab>
</CodeBlockTabs>

### 3. Change the imports [#3-change-the-imports]

Replace `fumadocs-ui/` with `@/components/docs/` in your imports, in `app/`, `components/` and `lib/`:

```tsx title="app/docs/layout.tsx"
import { DocsLayout } from "fumadocs-ui/layouts/docs"; // [!code --]
import { DocsLayout } from "@/components/docs/layouts/docs"; // [!code ++]
```

### 4. Remove Fumadocs UI's styles [#4-remove-fumadocs-uis-styles]

Delete the `fumadocs-ui/css/*` imports from your global stylesheet. The docscn components installed their styles into the same file, built from your shadcn/ui theme:

```css title="app/global.css"
@import "tailwindcss";
@import "fumadocs-ui/css/neutral.css"; /* [!code --] */
@import "fumadocs-ui/css/preset.css"; /* [!code --] */
```

### 5. Uninstall Fumadocs UI [#5-uninstall-fumadocs-ui]

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm uninstall fumadocs-ui
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm remove fumadocs-ui
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn remove fumadocs-ui
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun remove fumadocs-ui
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Then build the project. Pages, search, the OG image and Markdown routes keep working as before.

## What's different [#whats-different]

These are deliberate differences from Fumadocs UI:

| Fumadocs UI                                                      | docscn                                                                                                                                      |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| The `slots` prop on layouts and pages                            | Not supported. Edit your copy of the component instead.                                                                                     |
| `--color-fd-*` variables and `fd-*` utility classes              | Your shadcn/ui theme tokens (`--background`, `--primary`, `--sidebar-*`, ...). See [Theming and typography](/docs/theming).                 |
| Colour themes and CSS presets (`neutral.css`, `preset.css`, ...) | Not shipped. Change your shadcn/ui theme instead.                                                                                           |
| The `prose` class (`not-prose` to opt out)                       | `docs-typeset` (`not-docs-typeset` to opt out).                                                                                             |
| `fumadocs-ui/components/ui/*` (button, tabs, popover, ...)       | Your own shadcn/ui primitives in `components/ui/`.                                                                                          |
| `fumadocs-ui/components/sidebar/base`                            | The shadcn/ui `sidebar`. The docs and notebook layouts use `SidebarProvider` and `SidebarInset`, so `useSidebar()` works inside docs pages. |
| `createPageTreeRenderer` and `createLinkItemRenderer`            | `SidebarPageTree` and `SidebarLinkItem`, rendered with the shadcn/ui sidebar menu.                                                          |
| Providers for React Router, TanStack Start, Waku and Astro       | Next.js only (`provider/next`).                                                                                                             |
| Fumadocs UI's Radix UI variant                                   | Base UI only.                                                                                                                               |
| Deprecated props (`nav.component`, `sidebar.tabs`, ...)          | Not supported. Use the current props.                                                                                                       |
| The sidebar's desktop collapse state                             | Kept in shadcn/ui's `sidebar_state` cookie. Pass it to `sidebar.defaultOpen` to restore it on load.                                         |
| ⌘B                                                               | Toggles the sidebar, as in every shadcn/ui sidebar.                                                                                         |
| Hovering the edge of a collapsed sidebar shows it                | Not supported: the shadcn/ui sidebar slides away completely. Reopen it with the sidebar button or ⌘B.                                       |
| `Banner`'s layout offset                                         | Set as `--docs-banner-height` instead of `--fd-banner-height`.                                                                              |
| The notebook layout's width (centred, up to 97rem)               | The full width of the window, with the sidebar against its edge.                                                                            |
| The `aiChat` panel takes the table of contents' place            | Docked against the right edge of the window, beside the whole page. The table of contents hides while it's open.                            |

## Not supported yet [#not-supported-yet]

* **More layouts:** the flux, glass and spacious layouts.
* **Search dialogs** for Algolia and Orama Cloud. Build one from the [search dialog parts](/docs/components/search-dialog).

[Compatibility](/docs/compatibility) lists every Fumadocs UI module and its status.

# Theming and typography (/docs/theming)

> How docscn components use your shadcn/ui theme tokens, and the docs-typeset prose styles.

docscn has no colour themes of its own. Every component is styled with your shadcn/ui theme tokens, so changing your theme (or [picking a new one](https://ui.shadcn.com/themes)) changes your docs too.

## Theme tokens [#theme-tokens]

| Part                                   | Tokens                                                                           |
| -------------------------------------- | -------------------------------------------------------------------------------- |
| Page background and text               | `--background`, `--foreground`, `--muted-foreground`                             |
| Sidebar                                | `--sidebar`, `--sidebar-foreground`, `--sidebar-accent`, `--sidebar-border`, ... |
| Code blocks, cards and callouts        | `--card`, `--muted`, `--border`, `--radius`                                      |
| Links, active TOC items and highlights | `--foreground`, `--primary`                                                      |
| Search dialog and menus                | `--popover`, `--accent`                                                          |

Callouts add their own colours for the info, warning, success and idea types, and use `--destructive` for errors. Change them in `components/docs/components/callout.tsx`.

## Dark mode [#dark-mode]

`RootProvider` sets up [next-themes](https://github.com/pacocoursey/next-themes) with the same defaults as shadcn/ui's dark mode guide: the `class` attribute, the system theme by default, and no transitions while switching. Pass options with the `theme` prop:

```tsx
<RootProvider theme={{ defaultTheme: "dark" }}>{children}</RootProvider>
```

Press <kbd>D</kbd> to switch between light and dark mode (pass `theme={{ hotKey: false }}` to turn this off), or use the `ThemeSwitch` in the sidebar and navbar.

## Typography [#typography]

`DocsBody` adds the `docs-typeset` class, which styles the Markdown content inside it: headings, paragraphs, lists, links, inline code, blockquotes, tables, images and keyboard keys. The styles are built from your theme tokens, and the CLI added them to your global stylesheet inside `@layer components`, so utility classes still override them.

To keep an element and its children unstyled, add `not-docs-typeset`:

```mdx
<div className="not-docs-typeset">
  <MyWidget />
</div>
```

The class name is docscn's own, so it doesn't clash with `prose` from `@tailwindcss/typography` or with shadcn/typeset if your project uses them.

## Code highlighting [#code-highlighting]

Code blocks show Fumadocs MDX's default Shiki output, with light and dark themes. The `codeblock` component added the CSS that switches between them, plus the styles for [notations](https://shiki.style/packages/transformers) such as `// [!code highlight]`, `// [!code ++]`, `// [!code focus]` and line numbers. To change the Shiki themes, configure `rehypeCodeOptions` in Fumadocs MDX.