# Fumadocs CLI (the CLI tool for automating Fumadocs apps): User Guide
URL: /docs/cli
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/cli/index.mdx
The CLI tool that automates setups and installing components.
## Installation
Initialize a config for CLI:
```bash
npx @fumadocs/cli
```
```bash
pnpm dlx @fumadocs/cli
```
```bash
yarn dlx @fumadocs/cli
```
```bash
bun x @fumadocs/cli
```
You can change the output paths of components in the config.
### Components
Select and install components.
```bash
npx @fumadocs/cli add
```
```bash
pnpm dlx @fumadocs/cli add
```
```bash
yarn dlx @fumadocs/cli add
```
```bash
bun x @fumadocs/cli add
```
You can pass component names directly.
```bash
npx @fumadocs/cli add banner files
```
```bash
pnpm dlx @fumadocs/cli add banner files
```
```bash
yarn dlx @fumadocs/cli add banner files
```
```bash
bun x @fumadocs/cli add banner files
```
#### How the magic works?
The CLI fetches the latest version of component from the GitHub repository of Fumadocs.
When you install the component, it is guaranteed to be up-to-date.
In addition, it also transforms import paths.
Make sure to use the latest version of CLI
> This is highly Inspired by Shadcn UI.
### Customise
A simple way to customise Fumadocs layouts.
```bash
npx @fumadocs/cli customise
```
```bash
pnpm dlx @fumadocs/cli customise
```
```bash
yarn dlx @fumadocs/cli customise
```
```bash
bun x @fumadocs/cli customise
```
### Tree
Generate files tree for Fumadocs UI `Files` component, using the `tree` command from your terminal.
```bash
npx @fumadocs/cli tree ./my-dir ./output.tsx
```
```bash
pnpm dlx @fumadocs/cli tree ./my-dir ./output.tsx
```
```bash
yarn dlx @fumadocs/cli tree ./my-dir ./output.tsx
```
```bash
bun x @fumadocs/cli tree ./my-dir ./output.tsx
```
You can output MDX file too:
```bash
npx @fumadocs/cli tree ./my-dir ./output.mdx
```
```bash
pnpm dlx @fumadocs/cli tree ./my-dir ./output.mdx
```
```bash
yarn dlx @fumadocs/cli tree ./my-dir ./output.mdx
```
```bash
bun x @fumadocs/cli tree ./my-dir ./output.mdx
```
See help for further details:
```bash
npx @fumadocs/cli tree -h
```
```bash
pnpm dlx @fumadocs/cli tree -h
```
```bash
yarn dlx @fumadocs/cli tree -h
```
```bash
bun x @fumadocs/cli tree -h
```
#### Example Output
```tsx title="output.tsx"
import { File, Folder, Files } from 'fumadocs-ui/components/files';
export default (
);
```
# Fumadocs Core (core library of framework): Custom Source
URL: /docs/headless/custom-source
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/headless/custom-source.mdx
Build your own content source
## Introduction
**Fumadocs is very flexible.** You can integrate with any content source, even without an official adapter.
> This guide assumes you are experienced with Next.js App Router.
### Examples
You can see examples to use Fumadocs with a CMS, which allows a nice experience on publishing content, and real-time update without re-building the app.
* [BaseHub](https://github.com/fuma-nama/fumadocs-basehub)
* [Sanity](https://github.com/fuma-nama/fumadocs-sanity)
For a custom content source implementation, you will need:
### Page Tree
You can either hardcode the page tree, or write some code to generate one.
See [Definitions of Page Tree](/docs/headless/page-tree).
Pass your Page Tree to `DocsLayout` (usually in a `layout.tsx`).
```tsx title="layout.tsx"
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
The page tree is like a smarter "sidebar items", they will be referenced everywhere in the UI for navigation elements, such as the page footer.
### Docs Page
Same as a normal Next.js app, the code of your docs page is located in `[[...slug]]/page.tsx`.
#### SSG
Define the [`generateStaticParams`](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) function.
It should return a list of parameters (`params`) to populate the `[[...slug]]` catch-all route.
#### Body
In the main body of page, find the corresponding page according to the slug and render its content inside the `DocsPage` component.
You also need table of contents, which can be generated with your own implementation, or using the [`getTableOfContents`](/docs/headless/utils/get-toc) utility (Markdown/MDX only).
```tsx
import { DocsPage, DocsBody } from 'fumadocs-ui/page';
import { getPage } from './my-content-source';
import { notFound } from 'next/navigation';
export default function Page({ params }: { params: { slug?: string[] } }) {
const page = getPage(params.slug);
if (!page) notFound();
return (
{page.render()}
);
}
```
#### Metadata
Next.js offers a Metadata API for SEO, you can configure it with `generateMetadata` (similar as the code above).
### Document Search
This can be difficult considering your content may not be necessarily Markdown/MDX.
For Markdown and MDX, the built-in [Search API](/docs/headless/search/orama) is adequate for most use cases.
Otherwise, you will have to bring your own implementation.
We recommend 3rd party solutions like Algolia Search. They are more flexible than the built-in Search API, and is easier to integrate with remote sources.
Fumadocs offers a simple [Algolia Search Adapter](/docs/headless/search/algolia), which includes a search client to integrate with Fumadocs UI.
## MDX Remote
Fumadocs offers the **MDX Remote** package, it is a helper to integrate Markdown-based content sources with Fumadocs.
You can think it as a `next-mdx-remote` with built-in plugins for Fumadocs.
### Setup
```bash
npm install @fumadocs/mdx-remote
```
```bash
pnpm add @fumadocs/mdx-remote
```
```bash
yarn add @fumadocs/mdx-remote
```
```bash
bun add @fumadocs/mdx-remote
```
The main feature it offers is the MDX Compiler, it can compile MDX content to JSX nodes.
Since it doesn't use a bundler, there's some limitations:
* No imports and exports in MDX files.
It's compatible with Server Components. For example:
```tsx
import { compileMDX } from '@fumadocs/mdx-remote';
import { getPage } from './my-content-source';
import { DocsBody, DocsPage } from 'fumadocs-ui/page';
import { getMDXComponents } from '@/mdx-components';
export default async function Page({
params,
}: {
params: { slug?: string[] };
}) {
const page = getPage(params.slug);
const compiled = await compileMDX({
source: page.content,
});
const MdxContent = compiled.body;
return (
);
}
```
#### Images
On some platforms like Vercel, the original `public` folder (including static assets like images) will be removed after `next build`.
`compileMDX` might no longer be able to access local images in `public`.
When referencing images, make sure to use a URL.
# Fumadocs Core (core library of framework): Introduction
URL: /docs/headless
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/headless/index.mdx
Getting started with core library
## What is this?
Fumadocs Core offers server-side functions and headless components to build docs on any React.js frameworks like Next.js.
* Search (built-in: Orama, Algolia Search)
* Breadcrumb, Sidebar, TOC Components
* Remark/Rehype Plugins
* Additional utilities
It can be used without Fumadocs UI, in other words, it's headless.
For beginners and normal usages, use [Fumadocs UI](/docs/ui).
## Installation
No other dependencies required.
```bash
npm install fumadocs-core
```
```bash
pnpm add fumadocs-core
```
```bash
yarn add fumadocs-core
```
```bash
bun add fumadocs-core
```
For some components, a framework provider is needed:
```tsx tab="Next.js"
import type { ReactNode } from 'react';
import { NextProvider } from 'fumadocs-core/framework/next';
export function RootLayout({ children }: { children: ReactNode }) {
// or if you're using Fumadocs UI, use ``
return {children};
}
```
```tsx tab="React Router"
import type { ReactNode } from 'react';
import { ReactRouterProvider } from 'fumadocs-core/framework/react-router';
export function Root({ children }: { children: ReactNode }) {
return {children};
}
```
```tsx tab="Tanstack Start/Router"
import type { ReactNode } from 'react';
import { TanstackProvider } from 'fumadocs-core/framework/tanstack';
export function Root({ children }: { children: ReactNode }) {
return {children};
}
```
It offers simple document searching as well as components for building a
good docs.
# Fumadocs Core (core library of framework): Internationalization
URL: /docs/headless/internationalization
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/headless/internationalization.mdx
Support multiple languages in your documentation
## Introduction
Fumadocs core provides necessary middleware and options for i18n support.
You can define a config to share between utilities.
```ts title="lib/i18n.ts"
import type { I18nConfig } from 'fumadocs-core/i18n';
export const i18n: I18nConfig = {
defaultLanguage: 'en',
languages: ['en', 'cn'],
};
```
### Hide Locale Prefix
To hide the locale prefix (e.g. `/en/page` -> `/page`), use the `hideLocale` option.
```ts
import type { I18nConfig } from 'fumadocs-core/i18n';
export const i18n: I18nConfig = {
defaultLanguage: 'en',
languages: ['en', 'cn'],
hideLocale: 'default-locale',
};
```
| Mode | Description |
| ---------------- | -------------------------------------------------- |
| `always` | Always hide the prefix, detect locale from cookies |
| `default-locale` | Only hide the default locale |
| `never` | Never hide the prefix (default) |
Using always>}>
On `always` mode, locale is stored as a cookie (set by the middleware), which isn't optimal for static sites.
This may cause undesired cache problems, and need to pay extra attention on SEO to ensure search engines can index your pages correctly.
### Middleware
Redirects users to appropriate locale, it can be customised from `i18n.ts`.
```ts title="middleware.ts"
import { createI18nMiddleware } from 'fumadocs-core/i18n';
import { i18n } from '@/lib/i18n';
export default createI18nMiddleware(i18n);
export const config = {
// Matcher ignoring `/_next/` and `/api/`
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
```
> When `hideLocale` is enabled, it uses `NextResponse.rewrite` to hide locale prefixes.
# Fumadocs Core (core library of framework): Routing
URL: /docs/headless/page-conventions
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/headless/page-conventions.mdx
A shared convention for organizing your documents
This guide only applies for content sources that uses `loader()` API, such as Fumadocs MDX.
## Overview
While Next.js handles routing, Fumadocs generates **page slugs** and **sidebar items** (page tree) from your content directory using [`loader()`](/docs/headless/source-api).
You can define folders and pages similar to the file-system based routing of Next.js.
## File
A [MDX](https://mdxjs.com) or Markdown file, you can customise its frontmatter.
```mdx
---
title: My Page
description: Best document ever
icon: HomeIcon
full: true
---
## Learn More
```
| name | description |
| ------------- | -------------------------------------------------- |
| `title` | The title of page |
| `description` | The description of page |
| `icon` | The name of icon, see [Icons](#icons) |
| `full` | Fill all available space on the page (Fumadocs UI) |
You can use the [`schema`](/docs/mdx/collections#schema-1) option to add frontmatter properties.
### Slugs
The slugs of a page are generated from its file path.
| path (relative to content folder) | slugs |
| --------------------------------- | ----------------- |
| `./dir/page.mdx` | `['dir', 'page']` |
| `./dir/index.mdx` | `['dir']` |
## Folder
Organize multiple pages, you can create a [Meta file](#meta) to customise folders.
### Folder Group
By default, putting a file into folder will change its slugs.
You can wrap the folder name in parentheses to avoid impacting the slugs of child files.
| path (relative to content folder) | slugs |
| --------------------------------- | ---------- |
| `./(group-name)/page.mdx` | `['page']` |
## Meta
Customise folders by creating a `meta.json` file in the folder.
```json title="meta.json"
{
"title": "Display Name",
"icon": "MyIcon",
"pages": ["index", "getting-started"],
"defaultOpen": true
}
```
| name | description |
| ------------- | ------------------------------------- |
| `title` | Display name |
| `icon` | The name of icon, see [Icons](#icons) |
| `pages` | Folder items (see below) |
| `defaultOpen` | Open the folder by default |
### Pages
By default, folder items are sorted alphabetically.
You can add or control the order of items using `pages`, items are not included unless they are listed inside.
```json title="meta.json"
{
"title": "Name of Folder",
"pages": ["guide", "components", "---My Separator---", "./nested/page"]
}
```
#### Rest
Add a `...` item to include remaining pages (sorted alphabetically), or `z...a` for descending order.
```json title="meta.json"
{
"pages": ["guide", "..."]
}
```
You can add `!name` to prevent an item from being included.
```json title="meta.json"
{
"pages": ["guide", "...", "!components"]
}
```
#### Extract
You can extract the items from a folder with `...folder_name`.
```json title="meta.json"
{
"pages": ["guide", "...nested"]
}
```
#### Link
Use the syntax `[Text](url)` to insert links, or `[Icon][Text](url)` to add icon.
```json title="meta.json"
{
"pages": [
"[Vercel](https://vercel.com)",
"[Triangle][Vercel](https://vercel.com)"
]
}
```
## Icons
Since Fumadocs doesn't include an icon library, you have to convert the icon names to JSX elements in runtime, and render it as a component.
You can add an [`icon` handler](/docs/headless/source-api#icons) to `loader()`.
## Root Folder
Marks the folder as a root folder, only items in the opened root folder will be considered.
```json title="meta.json"
{
"title": "Name of Folder",
"description": "The description of root folder (optional)",
"root": true
}
```
For example, when you are opening a root folder `framework`, the other folders (e.g. `headless`) are not shown on the sidebar and other navigation elements.
Fumadocs UI renders root folders as [Sidebar Tabs](/docs/ui/navigation/sidebar#sidebar-tabs), which allows user to switch between them.
## Internationalization
You can add Markdown/meta files for different languages by attending `.{locale}` to your file name, like `page.cn.md` and `meta.cn.json`.
Make sure to create a file for the default locale first, the locale code is optional (e.g. both `get-started.mdx` and `get-started.en.mdx` are accepted).
# Fumadocs Core (core library of framework): Page Tree
URL: /docs/headless/page-tree
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/headless/page-tree.mdx
The structure of page tree.
Page tree is a tree structure that describes all navigation links, with other items like separator and folders.
It will be sent to the client and being referenced in navigation elements including the sidebar and breadcrumb.
Hence, you shouldn't store any sensitive or large data in page tree.
By design, page tree only contains necessary information of all pages and folders.
Unserializable data such as functions can't be passed to page tree.
## Conventions
The type definitions of page tree, for people who want to hardcode/generate it.
You can also import the type from Fumadocs.
```ts
import type { PageTree } from 'fumadocs-core/server';
const tree: PageTree.Root = {
// props
};
```
Certain nodes contain a `$ref` property, they are internal and not used when hardcoding it.
### Root
The initial root of page trees.
### Page
```json
{
"type": "page",
"name": "Quick Start",
"url": "/docs"
}
```
> External urls are also supported
### Folder
```json
{
"type": "folder",
"name": "Guide",
"index": {
"type": "page",
...
}
"children": [
...
]
}
```
### Separator
A label between items.
```json
{
"type": "separator",
"name": "Components"
}
```
## Icons
Icon is a `ReactElement`, supported by pages and folders.
# Fumadocs Core (core library of framework): loader()
URL: /docs/headless/source-api
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/headless/source-api.mdx
Turn a content source into a unified interface
## Usage
`loader()` provides an interface for Fumadocs to integrate with file-system based content sources.
### What it does?
* Generate page trees based on file system.
* Assign URL and slugs to each page.
* Output useful utilities to interact with content.
It doesn't rely on the real file system (zero `node:fs` usage), a virtual storage is also allowed.
You can use it with built-in content sources like Fumadocs MDX.
```ts
import { loader } from 'fumadocs-core/source';
import { docs } from '@/.source';
export const source = loader({
source: docs.toFumadocsSource(),
});
```
### URL
You can override the base URL, or specify a function to generate URL for each page.
```ts
import { loader } from 'fumadocs-core/source';
loader({
baseUrl: '/docs',
// or you can customise it with function
url(slugs, locale) {
if (locale) return '/' + [locale, 'docs', ...slugs].join('/');
return '/' + ['docs', ...slugs].join('/');
},
});
```
### Icons
Load the [icon](/docs/headless/page-conventions#icons) property specified by pages and meta files.
```ts
import { loader } from 'fumadocs-core/source';
import { icons } from 'lucide-react';
import { createElement } from 'react';
loader({
icon(icon) {
if (!icon) {
// You may set a default icon
return;
}
if (icon in icons) return createElement(icons[icon as keyof typeof icons]);
},
});
```
### I18n
Pass the `i18n` config to loader.
```ts title="lib/source.ts"
import { i18n } from '@/lib/i18n';
import { loader } from 'fumadocs-core/source';
export const source = loader({
i18n, // [!code highlight]
});
```
With i18n enabled, loader will generate a page tree for every locale.
When looking for a page, it fallbacks to default locale if the page doesn't exist for specified locale.
## Output
The loader outputs a source object.
### Get Page
Get page with slugs.
```ts
import { source } from '@/lib/source';
source.getPage(['slug', 'of', 'page']);
// with i18n
source.getPage(['slug', 'of', 'page'], 'locale');
```
### Get Pages
Get a list of page available for locale.
```ts
import { source } from '@/lib/source';
// from default locale
source.getPages();
// for a specific locale
source.getPages('locale');
```
### Page Tree
```ts
import { source } from '@/lib/source';
// without i18n
source.pageTree;
// with i18n
source.pageTree['locale'];
```
### Get from Node
The page tree nodes contain references to their original file path.
You can find their original page or meta file from the tree nodes.
```ts
import { source } from '@/lib/source';
source.getNodePage(pageNode);
source.getNodeMeta(folderNode);
```
### Params
A function to generate output for Next.js `generateStaticParams`.
The generated parameter names will be `slug: string[]` and `lang: string` (i18n only).
```ts title="app/[[...slug]]/page.tsx"
import { source } from '@/lib/source';
export function generateStaticParams() {
return source.generateParams();
}
```
### Language Entries
Get available languages and its pages.
```ts
import { source } from '@/lib/source';
// language -> pages
const entries = source.getLanguages();
```
## Deep Dive
As mentioned, Source API doesn't rely on real file systems.
During the process, your input source files will be parsed and form a virtual storage to avoid inconsistent behaviour between different OS.
### Transformer
To perform virtual file-system operations before processing, you can add a transformer.
```ts
import { loader } from 'fumadocs-core/source';
loader({
transformers: [
({ storage }) => {
storage.makeDir();
},
],
});
```
### Page Tree
The page tree is generated from your file system, some unnecessary information (e.g. unused frontmatter properties) will be filtered.
You can customise it using the `pageTree` option, such as attaching custom properties to nodes, or customising the display name of pages.
```tsx
import React from 'react';
import { loader } from 'fumadocs-core/source';
loader({
pageTree: {
attachFile(node, file) {
// you can access its file information
console.log(file?.data);
// JSX nodes are allowed
node.name = <>Some JSX Nodes here>;
return node;
},
},
});
```
### Custom Source
To plug your own content source, create a `Source` object.
It includes a `files` property which is an array of virtual files.
Each virtual file must contain its file path and corresponding data.
You can check type definitions for more info.
Since Source API doesn't rely on file system, file paths cannot be absolute or relative (for example, `./file.mdx` and `D://content/file.mdx` are not allowed).
Instead, pass the file paths like `file.mdx` and `content/file.mdx`.
```ts
import { Source } from 'fumadocs-core/source';
export function createMySource(): Source<{
metaData: { title: string; pages: string[] }; // Your custom type
pageData: { title: string; description: string }; // Your custom type
}> {
return {
files: [],
};
}
```
# Fumadocs MDX (the built-in content source): Async Mode
URL: /docs/mdx/async
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/async.mdx
Runtime compilation of content files.
## Introduction
By default, all Markdown and MDX files need to be pre-compiled first, the same constraint also applies on development server.
This may result in longer dev server start time for large docs sites, you can enable Async Mode on `doc` collections to improve this.
### Setup
Install required dependencies.
```bash
npm install @fumadocs/mdx-remote shiki
```
```bash
pnpm add @fumadocs/mdx-remote shiki
```
```bash
yarn add @fumadocs/mdx-remote shiki
```
```bash
bun add @fumadocs/mdx-remote shiki
```
Enable Async Mode.
```ts tab="Docs Collection"
import { defineDocs } from 'fumadocs-mdx/config';
export const docs = defineDocs({
dir: 'content/docs',
docs: {
async: true,
},
});
```
```ts tab="Doc Collection"
import { defineCollections } from 'fumadocs-mdx/config';
export const doc = defineCollections({
type: 'doc',
dir: 'content/docs',
async: true,
});
```
### Usage
Async Mode allows on-demand compilation of Markdown and MDX content, by moving the compilation process from build time to Next.js runtime.
However, you need to invoke the `load()` async function to load and compile content.
For example:
```tsx title="lib/source.ts"
import { loader } from 'fumadocs-core/source';
import { docs } from '@/.source';
export const source = loader({
baseUrl: '/docs',
source: docs.toFumadocsSource(),
});
```
```tsx title="page.tsx"
import { source } from '@/lib/source';
import { getMDXComponents } from '@/mdx-components';
const page = source.getPage(['...']);
if (page) {
// frontmatter properties are available
console.log(page.data);
// Markdown content requires await
const { body: MdxContent, toc } = await page.data.load();
console.log(toc);
return ;
}
```
When using Async Mode, we highly recommend to use 3rd party services to implement search, which usually has a better capability to handle massive amount of content to index.
### Constraints
It comes with some limitations on MDX features.
* No import/export allowed in MDX files, for MDX components, pass them from the `components` prop instead.
* Images must be referenced with URL (e.g. `/images/test.png`). Don't use **file paths** like `./image.png`, you should locate your images in `public` folder, and reference them with URLs.
# Fumadocs MDX (the built-in content source): Collections
URL: /docs/mdx/collections
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/collections.mdx
Collection of content data for your app
## Define Collections
Define a collection to parse a certain set of files.
```ts
import { defineCollections } from 'fumadocs-mdx/config';
import { z } from 'zod';
export const blog = defineCollections({
type: 'doc',
dir: './content/blog',
schema: z.object({
// schema
}),
// other options
});
```
### `type`
The accepted type of collection.
```ts
import { defineCollections } from 'fumadocs-mdx/config';
// only scan for json/yaml files
export const metaFiles = defineCollections({
type: 'meta',
// options
});
```
* `type: meta`
Accept JSON/YAML Files, available options:
* `type: doc`
Markdown/MDX Documents, available options:
### `dir`
Directories to scan input files.
### `schema`
The schema to validate file data (frontmatter on `doc` type, content on `meta` type).
```ts
import { defineCollections } from 'fumadocs-mdx/config';
import { z } from 'zod';
export const blog = defineCollections({
type: 'doc',
dir: './content/blog',
schema: z.object({
name: z.string(),
}),
});
```
> [Standard Schema](https://standardschema.dev) compatible libraries, including Zod are supported.
Note that the validation is done by build time, hence the output must be serializable.
You can also pass a function and receives the transform context.
```ts
import { defineCollections } from 'fumadocs-mdx/config';
import { z } from 'zod';
export const blog = defineCollections({
type: 'doc',
dir: './content/blog',
schema: (ctx) => {
return z.object({
name: z.string(),
testPath: z.string().default(
// original file path
ctx.path,
),
});
},
});
```
### `mdxOptions`
Customise MDX options on collection level.
```ts title="source.config.ts"
import { defineCollections, getDefaultMDXOptions } from 'fumadocs-mdx/config';
export const blog = defineCollections({
type: 'doc',
mdxOptions: {
// mdx options
},
});
```
By design, this will remove all default settings applied by your global config and Fumadocs MDX.
You have full control over MDX options.
You can use `getDefaultMDXOptions` to apply default configurations, it accepts the [extended MDX Options](/docs/mdx/mdx#extended).
```ts title="source.config.ts"
import { defineCollections, getDefaultMDXOptions } from 'fumadocs-mdx/config';
export const blog = defineCollections({
type: 'doc',
mdxOptions: getDefaultMDXOptions({
// extended mdx options
}),
});
```
> This API only available on `doc` type.
## Define Docs
Define a collection for Fumadocs.
```ts
import { defineDocs } from 'fumadocs-mdx/config';
export const docs = defineDocs({
dir: '/my/content/dir',
docs: {
// optional, options of `doc` collection
},
meta: {
// optional, options of `meta` collection
},
});
```
### `dir`
Instead of per collection, you should customise `dir` from `defineDocs`:
```ts
import { defineDocs } from 'fumadocs-mdx/config';
export const docs = defineDocs({
dir: 'my/content/dir',
});
```
### `schema`
You can extend the default Zod schema of `docs` and `meta`.
```ts
import { frontmatterSchema, metaSchema, defineDocs } from 'fumadocs-mdx/config';
import { z } from 'zod';
export const docs = defineDocs({
docs: {
schema: frontmatterSchema.extend({
index: z.boolean().default(false),
}),
},
meta: {
schema: metaSchema.extend({
// other props
}),
},
});
```
# Fumadocs MDX (the built-in content source): Global Options
URL: /docs/mdx/global
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/global.mdx
Customise Fumadocs MDX
## Global Options
Shared options of Fumadocs MDX.
```ts title="source.config.ts"
import { defineConfig } from 'fumadocs-mdx/config';
export default defineConfig({
// global options
});
```
### MDX Options
Customise the MDX processor options for MDX files.
```ts title="source.config.ts"
import { defineConfig } from 'fumadocs-mdx/config';
import rehypeKatex from 'rehype-katex';
import remarkMath from 'remark-math';
export default defineConfig({
mdxOptions: {
remarkPlugins: [remarkMath],
// When order matters
rehypePlugins: (v) => [rehypeKatex, ...v],
},
});
```
Some default options are applied by Fumadocs MDX, see [Extended MDX Options](/docs/mdx/mdx#extended) for available options.
# Fumadocs MDX (the built-in content source): Include
URL: /docs/mdx/include
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/include.mdx
Reuse content from other files.
## Usage
### Markdown
Specify the target Markdown file path in `` tag (relative to the Markdown file itself).
```mdx title="page.mdx"
./another.mdx
```
This will display the content from target file (e.g. `another.mdx`).
### CodeBlock
For other types of files, it will become a codeblock:
```mdx title="page.mdx"
./script.ts
./script.ts
```
### `cwd`
Resolve relative paths from cwd instead of Markdown file:
```mdx
./script.ts
```
# Fumadocs MDX (the built-in content source): Introduction
URL: /docs/mdx
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/index.mdx
What is Fumadocs MDX?
Fumadocs MDX is the official content source of Fumadocs.
It provides the tool for Next.js to transform content into type-safe data, similar to Content Collections.
This library made for Next.js, you can use it to handle blog and other contents.
## Getting Started
Setup Fumadocs MDX for your Next.js application.
```bash
npm install fumadocs-mdx @types/mdx
```
```bash
pnpm add fumadocs-mdx @types/mdx
```
```bash
yarn add fumadocs-mdx @types/mdx
```
```bash
bun add fumadocs-mdx @types/mdx
```
Add the plugin to your `next.config.mjs` file.
```js
import { createMDX } from 'fumadocs-mdx/next';
const withMDX = createMDX();
/** @type {import('next').NextConfig} */
const config = {
reactStrictMode: true,
};
export default withMDX(config);
```
The Next.js config must be a `.mjs` file since Fumadocs is ESM-only.
### Defining Collections
**Collection** refers to a collection containing a certain type of files, you can define collections by creating a `source.config.ts` file.
Fumadocs MDX transforms collections into arrays of type-safe data, accessible in your app, available collections:
Compile Markdown & MDX files into a React Server Component, with useful properties like **Table of Contents**.
```ts title="source.config.ts"
import { defineCollections } from 'fumadocs-mdx/config';
export const test = defineCollections({
type: 'doc',
dir: 'content/docs',
});
```
Transform YAML/JSON files into an array of data.
```ts title="source.config.ts"
import { defineCollections } from 'fumadocs-mdx/config';
export const test = defineCollections({
type: 'meta',
dir: 'content/docs',
});
```
Combination of `meta` and `doc` collections, which is needed for Fumadocs.
```ts title="source.config.ts"
import { defineDocs } from 'fumadocs-mdx/config';
export const docs = defineDocs({
dir: 'content/docs',
docs: {
// options for `doc` collection
},
meta: {
// options for `meta` collection
},
});
```
For example, a `doc` collection will transform the `.md` and `.mdx` files:
### Output Folder
A `.source` folder is generated in root directory when you run `next dev` or `next build`, it contains all output data and types, you should add it to `.gitignore`.
The `fumadocs-mdx` command also generates types for `.source` folder, add it as a post install script to ensure types are generated when initializing the project.
```json title="package.json"
{
"scripts": {
"postinstall": "fumadocs-mdx"
}
}
```
### Accessing Collections
**Collection Output** is the generated data of a collection, it can have a different type/shape depending on the collection type and schema.
You can access the collection output from `.source` folder with its original name:
```ts tab="source.config.ts"
import { defineDocs } from 'fumadocs-mdx/config';
export const docs = defineDocs({
dir: 'content/docs',
docs: {
// options for `doc` collection
},
meta: {
// options for `meta` collection
},
});
```
```ts tab="Usage"
import { docs } from '@/.source';
console.log(docs);
```
> Make sure you are importing from `.source` rather than `source.config.ts`, we will import it with `@/.source` import alias in this guide.
## Integrate with Fumadocs
Create a `docs` collection and use the `toFumadocsSource()` function of its output.
```ts title="lib/source.ts"
import { docs } from '@/.source';
import { loader } from 'fumadocs-core/source';
export const source = loader({
baseUrl: '/docs',
source: docs.toFumadocsSource(),
});
```
> You can do the same for multiple `docs` collections.
Generally, you'll interact with the collection through [`loader()`](/docs/headless/source-api#output).
```tsx
import { source } from '@/lib/source';
const page = source.getPage(['slugs']);
if (page) {
// access page data [!code highlight]
console.log(page.data);
// frontmatter properties are also inside [!code highlight]
console.log(page.data.title);
}
```
To render the page, use `page.data.body` as a component.
```tsx
import { getMDXComponents } from '@/mdx-components';
const MDX = page.data.body;
// set your MDX components with `components` prop
return ;
```
## FAQ
### Built-in Properties
These properties are exported from MDX files by default.
| Property | Description |
| ---------------- | ----------------------------------------------- |
| `frontmatter` | Frontmatter |
| `toc` | Table of Contents |
| `structuredData` | Structured Data, useful for implementing search |
### Customise Frontmatter
Use the [`schema`](/docs/mdx/collections#schema-1) option to pass a validation schema to validate frontmatter and define its output properties.
### MDX Plugins
For other customisation needs such as Syntax Highlighting, see [MDX Options](/docs/mdx/mdx).
# Fumadocs MDX (the built-in content source): Last Modified Time
URL: /docs/mdx/last-modified
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/last-modified.mdx
Output the last modified time of a document
## Usage
This feature is not enabled by default, you can enable this from config file. Notice that it only supports Git as version control.
Please ensure you have Git installed on your machine, and **the repository is not shallow cloned**, as it relies on your local Git history.
```ts title="source.config.ts"
import { defineConfig } from 'fumadocs-mdx/config';
export default defineConfig({
lastModifiedTime: 'git', // [!code highlight]
});
```
### Access the Property
After doing this, a `lastModified` number will be exported for each document, you can convert it to a JavaScript Date object.
```ts
import { source } from '@/lib/source';
const page = source.getPage(['...']);
console.log(new Date(page.data.lastModified));
// or with async mode:
const { lastModified } = await page.data.load();
console.log(new Date(lastModified));
```
# Fumadocs MDX (the built-in content source): MDX Options
URL: /docs/mdx/mdx
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/mdx.mdx
Configure MDX processor for Fumadocs MDX
## Customising MDX Processor
Fumadocs MDX uses [MDX Compiler](https://mdxjs.com/packages/mdx) to compile MDX files into JavaScript files.
You can customise it on [Global Config](/docs/mdx/global#mdx-options) or [Collection Config](/docs/mdx/collections#mdxoptions).
## Extended MDX Options \[#extended]
Fumadocs MDX will apply some default MDX options, to make features like **syntax highlighting** work out of the box.
To allow overriding the defaults, Fumadocs MDX's `mdxOptions` option accepts **Extended MDX Options** on top of [`ProcessorOptions`](https://mdxjs.com/packages/mdx/#processoroptions).
You can see the additional options below:
### Remark Plugins
These plugins are applied by default:
* [Remark Image](/docs/headless/mdx/remark-image) - Handle images
* [Remark Heading](/docs/headless/mdx/headings) - Extract table of contents
* [Remark Structure](/docs/headless/mdx/structure) - Generate search indexes
* Remark Exports - Exports the output generated by remark plugins above
You can add other remark plugins with:
```ts tab="Global Config"
import { defineConfig } from 'fumadocs-mdx/config';
import { myPlugin } from './remark-plugin';
export default defineConfig({
mdxOptions: {
remarkPlugins: [myPlugin],
// You can also pass a function to control the order of remark plugins.
remarkPlugins: (v) => [myPlugin, ...v],
},
});
```
```ts tab="Collection Config"
import { defineCollections, getDefaultMDXOptions } from 'fumadocs-mdx/config';
import { myPlugin } from './remark-plugin';
export const blog = defineCollections({
type: 'doc',
mdxOptions: getDefaultMDXOptions({
remarkPlugins: [myPlugin],
// You can also pass a function to control the order of remark plugins.
remarkPlugins: (v) => [myPlugin, ...v],
}),
});
```
### Rehype Plugins
These plugins are applied by default:
* [Rehype Code](/docs/headless/mdx/rehype-code) - Syntax highlighting
Same as remark plugins, you can pass an array or a function to add other rehype plugins.
```ts tab="Global Config"
import { defineConfig } from 'fumadocs-mdx/config';
import { myPlugin } from './rehype-plugin';
export default defineConfig({
mdxOptions: {
rehypePlugins: (v) => [myPlugin, ...v],
},
});
```
```ts tab="Collection Config"
import { defineCollections, getDefaultMDXOptions } from 'fumadocs-mdx/config';
import { myPlugin } from './rehype-plugin';
export const blog = defineCollections({
type: 'doc',
mdxOptions: getDefaultMDXOptions({
rehypePlugins: (v) => [myPlugin, ...v],
}),
});
```
### Customise Built-in Plugins
Customise the options of built-in plugins like:
```ts tab="Global Config"
import { defineConfig } from 'fumadocs-mdx/config';
export default defineConfig({
mdxOptions: {
rehypeCodeOptions: {
// options
},
remarkImageOptions: {
// options
},
remarkHeadingOptions: {
// options
},
},
});
```
```ts tab="Collection Config"
import { defineCollections, getDefaultMDXOptions } from 'fumadocs-mdx/config';
export const blog = defineCollections({
type: 'doc',
mdxOptions: getDefaultMDXOptions({
rehypeCodeOptions: {
// options
},
remarkImageOptions: {
// options
},
remarkHeadingOptions: {
// options
},
}),
});
```
### Export Properties from `vfile.data`
Some remark plugins store their output in `vfile.data` (an compile-time memory) which cannot be accessed from your code.
Fumadocs MDX applies a remark plugin that turns `vfile.data` properties into ESM exports, so that you can access these properties when importing the MDX file.
You can define additional properties to be exported.
```ts tab="Global Config"
import { defineConfig } from 'fumadocs-mdx/config';
export default defineConfig({
mdxOptions: {
valueToExport: ['dataName'],
},
});
```
```ts tab="Collection Config"
import { defineCollections, getDefaultMDXOptions } from 'fumadocs-mdx/config';
export const blog = defineCollections({
type: 'doc',
mdxOptions: getDefaultMDXOptions({
valueToExport: ['dataName'],
}),
});
```
By default, it includes:
* `toc` for the Remark Heading plugin
* `structuredData` for the Remark Structure Plugin
* `frontmatter` for the frontmatter of MDX (using `gray-matter`)
# Fumadocs MDX (the built-in content source): Use as Page
URL: /docs/mdx/page
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/page.mdx
Use MDX file as a page
## Setup
You can use `page.mdx` instead of `page.tsx` for creating a new page under the app directory.
However, it doesn't have MDX components by default so you have to provide them:
```tsx title="mdx-components.tsx"
import defaultMdxComponents from 'fumadocs-ui/mdx';
import type { MDXComponents } from 'mdx/types';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultMdxComponents, // for Fumadocs UI
...components,
};
}
// export a `useMDXComponents()` that returns MDX components
export const useMDXComponents = getMDXComponents; // [!code ++]
```
```ts title="source.config.ts"
import { defineConfig } from 'fumadocs-mdx/config';
export default defineConfig({
mdxOptions: {
// Path to import your `mdx-components.tsx` above. [!code ++]
providerImportSource: '@/mdx-components',
},
});
```
### Usage
```mdx title="app/test/page.mdx"
{/* this will enable Typography styles of Fumadocs UI */}
export { withArticle as default } from 'fumadocs-ui/page';
## Hello World
```
# Fumadocs MDX (the built-in content source): Performance
URL: /docs/mdx/performance
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/performance.mdx
The performance of Fumadocs MDX
## Overview
Fumadocs MDX is a bundler plugin, in other words, it has a higher performance bottleneck.
With bundlers like Webpack and Turbopack, it is enough for large docs sites with nearly 500+ MDX files, which is sufficient for almost all use cases.
Since Fumadocs MDX works with your bundler, you can import any files including client components in your MDX files.
This allows a high flexibility and ensures everything is optimized by default.
### Image Optimization
Fumadocs MDX resolves images into static imports with [Remark Image](/docs/headless/mdx/remark-image).
Therefore, your images will be optimized automatically by the Next.js Image API.
```mdx

or in public folder

```
Yields:
```mdx
import HelloImage from './hello.png';
```

## Caveats
Although Fumadocs MDX can handle nearly 500+ files, it could be slow and inefficient.
A huge amount of MDX files can cause an extremely high memory usage during build and development mode.
This is because of:
* Bundlers do a lot of work under the hood to bundle MDX and JavaScript files and optimize performance.
* Bundlers are not supposed to compile hundreds of MDX files.
### Solutions
The main solution is to make the compilation on-demand, such that content is only loaded when it's being requested.
#### Remote Source
Remote sources don't need to pre-compile MDX files, it can compile them on-demand with SSG which can **highly increase your build speed.**
However, you cannot use import in MDX files anymore.
See [Custom Source](/docs/headless/custom-source) for configuring remote sources.
#### Async Mode
See [Async Mode](/docs/mdx/async).
# Fumadocs MDX (the built-in content source): Next.js Loader
URL: /docs/mdx/plugin
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/mdx/plugin.mdx
Customise the Next.js loader
## Plugin Options
Fumadocs MDX offers loaders and a Fumadocs [Source API](/docs/headless/source-api) adapter to integrate with Fumadocs.
You can configure the plugin by passing options to `createMDX` in `next.config.mjs`.
### Config Path
Customise the path of config file.
```ts
import { createMDX } from 'fumadocs-mdx/next';
const withMDX = createMDX({
configPath: './my-config.ts',
});
```
### Development Server
When running in development mode (`next dev`), a file watcher will be started to watch for changes.
It automatically re-generates the index file in `.source` folder, ensuring Next.js hot reload is working properly.
# Fumadocs Framework: Comparisons
URL: /docs/ui/comparisons
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/comparisons.mdx
How is Fumadocs different from other existing frameworks?
## Nextra
Fumadocs is highly inspired by Nextra. For example, the Routing Conventions. That is why
`meta.json` also exists in Fumadocs.
Nextra is more opinionated than Fumadocs. Fumadocs is accelerated by App Router. As a result, It provides many server-side functions, and you have to
configure things manually compared to simply editing a configuration file.
Fumadocs works great if you want more control over everything, such as
adding it to an existing codebase or implementing advanced routing.
### Feature Table
| Feature | Fumadocs | Nextra |
| ------------------- | ------------ | ------------------------- |
| Static Generation | Yes | Yes |
| Cached | Yes | Yes |
| Light/Dark Mode | Yes | Yes |
| Syntax Highlighting | Yes | Yes |
| Table of Contents | Yes | Yes |
| Full-text Search | Yes | Yes |
| i18n | Yes | Yes |
| Last Git Edit Time | Yes | Yes |
| Page Icons | Yes | Yes, via `_meta.js` files |
| RSC | Yes | Yes |
| Remote Source | Yes | Yes |
| SEO | Via Metadata | Yes |
| Built-in Components | Yes | Yes |
| RTL Layout | Yes | Yes |
### Additional Features
Features supported via 3rd party libraries like [TypeDoc](https://typedoc.org) will not be listed here.
| Feature | Fumadocs | Nextra |
| -------------------------- | -------- | ------ |
| OpenAPI Integration | Yes | No |
| TypeScript Docs Generation | Yes | No |
| TypeScript Twoslash | Yes | Yes |
## Mintlify
Mintlify is a documentation service, as compared to Fumadocs, it offers a free tier but isn't completely free and open source.
Fumadocs is not as powerful as Mintlify, for example, the OpenAPI integration of Mintlify.
As the creator of Fumadocs, I wouldn't recommend switching to Fumadocs from Mintlify if you're satisfied with the current way you build docs.
However, I believe Fumadocs is a suitable tool for all Next.js developers who want to have elegant docs.
## Docusaurus
Docusaurus is a powerful framework based on React.js. It offers many cool
features with plugins and custom themes.
### Better DX
Since Fumadocs is built on the top of Next.js, you'll have to start the Next.js dev
server every time to review changes, and initial boilerplate code is relatively more
compared to Docusaurus.
For a simple docs, Docusaurus might be a better choice if you don't need any Next.js specific functionality.
However, when you want to use Next.js, or seek extra customizability like tuning default UI components, Fumadocs could be a better choice.
### Plugins
You can easily achieve many things with plugins, their ecosystem is indeed larger and maintained by many contributors.
In comparison, the flexibility of Fumadocs allows you to implement them on your own, it may take longer to tune it to your satisfaction.
# Fumadocs Framework: Overview
URL: /docs/ui/customisation
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/customisation.mdx
An overview of Fumadocs UI
## Architecture
| | |
| ------------- | ------------------------------------------------------- |
| **Sidebar** | Display site title and navigation elements. |
| **Page Tree** | Passed by you, mainly rendered as the items of sidebar. |
| **Docs Page** | All content of the page. |
| **TOC** | Navigation within the article. |
## Customisation
### Layouts
You can use the exposed options of different layouts:
Layout for docs
Layout for docs content
A more compact version of Docs Layout
Layout for other pages
### Components
Fumadocs UI also offers styled components for interactive examples to enhance your docs, you can customise them with exposed props like `style` and `className`.
See [Components](/docs/ui/components).
### Design System
Since the design system is built on Tailwind CSS, you can customise it [with CSS Variables](/docs/ui/theme#colors).
### CLI
Fumadocs CLI is a tool that installs components to your codebase, similar to Shadcn UI.
```bash
npx @fumadocs/cli
```
```bash
pnpm dlx @fumadocs/cli
```
```bash
yarn dlx @fumadocs/cli
```
```bash
bun x @fumadocs/cli
```
Use it to install Fumadocs UI components:
```bash
npx @fumadocs/cli add
```
```bash
pnpm dlx @fumadocs/cli add
```
```bash
yarn dlx @fumadocs/cli add
```
```bash
bun x @fumadocs/cli add
```
Or customise layouts:
```bash
npx @fumadocs/cli customise
```
```bash
pnpm dlx @fumadocs/cli customise
```
```bash
yarn dlx @fumadocs/cli customise
```
```bash
bun x @fumadocs/cli customise
```
# Fumadocs Framework: Quick Start
URL: /docs/ui
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/index.mdx
Getting Started with Fumadocs
## Introduction
Fumadocs (Foo-ma docs) is a **documentation framework** based on Next.js, designed to be fast, flexible,
and composes seamlessly into Next.js App Router.
Fumadocs has different parts:
} title="Fumadocs Core">
Handles most of the logic, including document search, content source adapters, and Markdown extensions.
} title="Fumadocs UI">
The default theme of Fumadocs offers a beautiful look for documentation sites and interactive components.
} title="Content Source">
The source of your content, can be a CMS or local data layers like [Fumadocs MDX](/docs/mdx) (the official content source).
} title="Fumadocs CLI">
A command line tool to install UI components and automate things, useful for customizing layouts.
Read our in-depth [What is Fumadocs](/docs/ui/what-is-fumadocs) introduction.
### Terminology
**Markdown/MDX:** Markdown is a markup language for creating formatted text. Fumadocs supports Markdown and MDX (superset of Markdown) out-of-the-box.
Although not required, some basic knowledge of Next.js App Router would be useful for further customisations.
## Automatic Installation
A minimum version of Node.js 18 required, note that Node.js 23.1 might have problems with Next.js production build.
```bash tab="npm"
npm create fumadocs-app
```
```bash tab="pnpm"
pnpm create fumadocs-app
```
```bash tab="yarn"
yarn create fumadocs-app
```
```bash tab="bun"
bun create fumadocs-app
```
It will ask you:
* the React.js framework to use (the docs is only written for Next.js).
* the content source to use.
A new fumadocs app should be initialized. Now you can start hacking!
You can follow the [Manual Installation](/docs/ui/manual-installation) guide to get started.
### Enjoy!
Create your first MDX file in the docs folder.
```mdx title="content/docs/index.mdx"
---
title: Hello World
---
## Yo what's up
```
Run the app in development mode and see [http://localhost:3000/docs](http://localhost:3000/docs).
```bash
npm run dev
```
```bash
pnpm run dev
```
```bash
yarn dev
```
```bash
bun run dev
```
## FAQ
Some common questions you may encounter.
You can change the base route of docs (e.g. from `/docs/page` to `/info/page`).
Since Fumadocs uses Next.js App Router, you can simply rename the route:
And tell Fumadocs to use the new route in `source.ts`:
```ts title="lib/source.ts"
import { loader } from 'fumadocs-core/source';
export const source = loader({
baseUrl: '/info',
// other options
});
```
Next.js turns dynamic route into static routes when `generateStaticParams` is configured.
Hence, it is as fast as static pages.
You can enable Static Exports on Next.js to get a static build output. (Notice that Route Handler doesn't work with static export, you have to configure static search)
Same as managing layouts in Next.js App Router, remove the original MDX file from content directory (`/content/docs`).
This ensures duplicated pages will not cause errors.
Now, You can add the page to another route group, which isn't a descendant of docs layout.
For example, to replace `/docs/test`:
For `/docs`, you need to change the catch-all route to be non-optional:
We recommend to use [Sidebar Tabs](/docs/ui/navigation/sidebar#sidebar-tabs).
## Learn More
New to here? Don't worry, we are welcome for your questions.
If you find anything confusing, please give your feedback on [Github Discussion](https://github.com/fuma-nama/fumadocs/discussions)!
### Writing Content
For authoring docs, make sure to read:
Fumadocs has some additional features for authoring content.
Learn how to customise navigation links and sidebar items.
Learn how to organise content.
# Fumadocs Framework: Internationalization
URL: /docs/ui/internationalization
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/internationalization.mdx
Support multiple languages in your documentation
Fumadocs is not a full-powered i18n library, it manages only its own components and utilities.
You can use other libraries like [next-intl](https://github.com/amannn/next-intl) for the rest of your app.
Read the [Next.js Docs](https://nextjs.org/docs/app/building-your-application/routing/internationalization) to learn more about implementing I18n in Next.js.
## Manual Setup
Define the i18n configurations in a file, we will import it with `@/ilb/i18n` in this guide.
```ts title="lib/i18n.ts"
import type { I18nConfig } from 'fumadocs-core/i18n';
export const i18n: I18nConfig = {
defaultLanguage: 'en',
languages: ['en', 'cn'],
};
```
> See [customisable options](/docs/headless/internationalization).
Pass it to the source loader.
```ts title="lib/source.ts"
import { i18n } from '@/lib/i18n';
import { loader } from 'fumadocs-core/source';
export const source = loader({
i18n, // [!code highlight]
// other options
});
```
And update Fumadocs UI layout options.
```tsx title="app/layout.config.tsx"
import { i18n } from '@/lib/i18n';
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export function baseOptions(locale: string): BaseLayoutProps {
return {
i18n,
// different props based on `locale`
};
}
```
### Middleware
Create a middleware that redirects users to appropriate locale.
```ts title="middleware.ts"
import { createI18nMiddleware } from 'fumadocs-core/i18n';
import { i18n } from '@/lib/i18n';
export default createI18nMiddleware(i18n);
export const config = {
// Matcher ignoring `/_next/` and `/api/`
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
```
The default middleware is optional, you can instead use your own middleware or the one provided by i18n libraries.
When using custom middleware, make sure the locale is correctly passed to Fumadocs.
You may also want to [customise page URLs](/docs/headless/source-api#url) from `loader()`.
### Routing
Create a `/app/[lang]` folder, and move all files (e.g. `page.tsx`, `layout.tsx`) from `/app` to the folder.
Provide UI translations and other config to ``.
Note that only English translations are provided by default.
```tsx title="app/[lang]/layout.tsx"
import { RootProvider } from 'fumadocs-ui/provider';
import type { Translations } from 'fumadocs-ui/i18n';
const cn: Partial = {
search: 'Translated Content',
// other translations
};
// available languages that will be displayed on UI
// make sure `locale` is consistent with your i18n config
const locales = [
{
name: 'English',
locale: 'en',
},
{
name: 'Chinese',
locale: 'cn',
},
];
export default async function RootLayout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: React.ReactNode;
}) {
const lang = (await params).lang;
return (
{children}
);
}
```
### Pass Locale
Pass the locale to Fumadocs in your pages and layouts.
```tsx title="/app/[lang]/(home)/layout.tsx" tab="Home Layout"
import type { ReactNode } from 'react';
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions } from '@/app/layout.config';
export default async function Layout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
const { lang } = await params;
return {children};
}
```
```tsx title="/app/[lang]/docs/layout.tsx" tab="Docs Layout"
import type { ReactNode } from 'react';
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { baseOptions } from '@/app/layout.config';
export default async function Layout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
const { lang } = await params;
return (
{children}
);
}
```
```ts title="page.tsx" tab="Docs Page"
import { source } from '@/lib/source';
export default async function Page({
params,
}: {
params: Promise<{ lang: string; slug?: string[] }>;
}) {
const { slug, lang } = await params;
// get page
source.getPage(slug); // [!code --]
source.getPage(slug, lang); // [!code ++]
// get pages
source.getPages(); // [!code --]
source.getPages(lang); // [!code ++]
}
```
Using another name for lang dynamic segment?>}>
If you're using another name like `app/[locale]`, you also need to update `generateStaticParams()` in docs page:
```tsx
export function generateStaticParams() {
return source.generateParams(); // [!code --]
return source.generateParams('slug', 'locale'); // [!code ++] new param name
}
```
### Search
Configure i18n on your search solution.
* **Built-in Search (Orama):**
For [Supported Languages](https://docs.orama.com/open-source/supported-languages#officially-supported-languages), no further changes are needed.
Otherwise, additional config is required (e.g. Chinese & Japanese). See [Special Languages](/docs/headless/search/orama#special-languages).
* **Cloud Solutions (e.g. Algolia):**
They usually have official support for multilingual.
## Writing Documents
You can add Markdown/meta files for different languages by attending `.{locale}` to your file name, like `page.cn.md` and `meta.cn.json`.
Make sure to create a file for the default locale first, the locale code is optional (e.g. both `get-started.mdx` and `get-started.en.mdx` are accepted).
## Navigation
Fumadocs only handles navigation for its own layouts (e.g. sidebar).
For other places, you can use the `useParams` hook to get the locale from url, and attend it to `href`.
```tsx
import Link from 'next/link';
import { useParams } from 'next/navigation';
const { lang } = useParams();
return This is a link;
```
In addition, the [`fumadocs-core/dynamic-link`](/docs/headless/components/link#dynamic-hrefs) component supports dynamic hrefs, you can use it to attend the locale prefix.
It is useful for Markdown/MDX content.
```mdx title="content.mdx"
import { DynamicLink } from 'fumadocs-core/dynamic-link';
This is a link
```
# Fumadocs Framework: Manual Installation
URL: /docs/ui/manual-installation
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/manual-installation.mdx
Add Fumadocs to existing projects.
Before continuing, make sure:
* Next.js 15 and Tailwind CSS 4 are configured.
## Getting Started
```bash
npm install fumadocs-ui fumadocs-core
```
```bash
pnpm add fumadocs-ui fumadocs-core
```
```bash
yarn add fumadocs-ui fumadocs-core
```
```bash
bun add fumadocs-ui fumadocs-core
```
### MDX Components
```tsx title="mdx-components.tsx"
import defaultMdxComponents from 'fumadocs-ui/mdx';
import type { MDXComponents } from 'mdx/types';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultMdxComponents,
...components,
};
}
```
### Content Source
Fumadocs supports different content sources, including Fumadocs MDX and [Content Collections](/docs/headless/content-collections).
Fumadocs MDX is our official content source, you can configure it with:
```bash
npm install fumadocs-mdx @types/mdx
```
```bash
pnpm add fumadocs-mdx @types/mdx
```
```bash
yarn add fumadocs-mdx @types/mdx
```
```bash
bun add fumadocs-mdx @types/mdx
```
```js tab="next.config.mjs"
import { createMDX } from 'fumadocs-mdx/next';
const withMDX = createMDX();
/** @type {import('next').NextConfig} */
const config = {
reactStrictMode: true,
};
export default withMDX(config);
```
```ts tab="source.config.ts"
import { defineDocs } from 'fumadocs-mdx/config';
export const docs = defineDocs({
dir: 'content/docs',
});
```
```json tab="package.json"
{
"scripts": {
"postinstall": "fumadocs-mdx" // [!code ++]
}
}
```
Finally, to access your content:
```ts title="lib/source.ts"
// .source folder will be generated when you run `next dev`
import { docs } from '@/.source';
import { loader } from 'fumadocs-core/source';
export const source = loader({
baseUrl: '/docs',
source: docs.toFumadocsSource(),
});
```
### Root Layout
Wrap the entire application inside [Root Provider](/docs/ui/layouts/root-provider), and add required styles to `body`.
```tsx
import { RootProvider } from 'fumadocs-ui/provider';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
### Styles
Add the following to `global.css`.
```css title="Tailwind CSS"
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
```
> It doesn't come with a default font, you may choose one from `next/font`.
### Layout
Create a `app/layout.config.tsx` file to put the shared options for our layouts.
```tsx title="app/layout.config.tsx"
import { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export const baseOptions: BaseLayoutProps = {
nav: {
title: 'My App',
},
};
```
Create a folder `/app/docs` for our docs, and give it a proper layout.
```tsx title="app/docs/layout.tsx"
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions } from '@/app/layout.config';
export default function Layout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
> `pageTree` refers to Page Tree, it should be provided by your content source.
### Page
Create a catch-all route `/app/docs/[[...slug]]` for docs pages.
In the page, wrap your content in the [Page](/docs/ui/layouts/page) component.
```tsx title="app/docs/\[\[...slug]]/page.tsx" tab="Fumadocs MDX"
import { source } from '@/lib/source';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/mdx-components';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
const MDX = page.data.body;
return (
{page.data.title}{page.data.description}
);
}
export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
return {
title: page.data.title,
description: page.data.description,
};
}
```
```tsx title="app/docs/\[\[...slug]]/page.tsx" tab="Content Collections"
import { source } from '@/lib/source';
import type { Metadata } from 'next';
import {
DocsPage,
DocsBody,
DocsTitle,
DocsDescription,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import { MDXContent } from '@content-collections/mdx/react';
import { getMDXComponents } from '@/mdx-components';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) {
notFound();
}
return (
{page.data.title}{page.data.description}
);
}
export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
return {
title: page.data.title,
description: page.data.description,
} satisfies Metadata;
}
```
### Search
Use the default document search based on Orama.
```ts title="app/api/search/route.ts"
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';
export const { GET } = createFromSource(source);
```
Learn more about [Document Search](/docs/headless/search).
### Done
You can start the dev server and create MDX files.
```mdx title="content/docs/index.mdx"
---
title: Hello World
---
## Introduction
I love Anime.
```
## Deploying
It should work out-of-the-box with Vercel & Netlify.
### Docker Deployment
If you want to deploy your Fumadocs app using Docker with **Fumadocs MDX configured**, make sure to add the `source.config.ts` file to the `WORKDIR` in the Dockerfile.
The following snippet is taken from the official [Next.js Dockerfile Example](https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile):
```zsh title="Dockerfile"
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* source.config.ts ./
```
This ensures Fumadocs MDX can access your configuration file during builds.
# Fumadocs Framework: Routing
URL: /docs/ui/page-conventions
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/page-conventions.mdx
A shared convention for organizing your documents
This guide only applies for content sources that uses `loader()` API, such as Fumadocs MDX.
## Overview
While Next.js handles routing, Fumadocs generates **page slugs** and **sidebar items** (page tree) from your content directory using [`loader()`](/docs/headless/source-api).
You can define folders and pages similar to the file-system based routing of Next.js.
## File
A [MDX](https://mdxjs.com) or Markdown file, you can customise its frontmatter.
```mdx
---
title: My Page
description: Best document ever
icon: HomeIcon
full: true
---
## Learn More
```
| name | description |
| ------------- | -------------------------------------------------- |
| `title` | The title of page |
| `description` | The description of page |
| `icon` | The name of icon, see [Icons](#icons) |
| `full` | Fill all available space on the page (Fumadocs UI) |
You can use the [`schema`](/docs/mdx/collections#schema-1) option to add frontmatter properties.
### Slugs
The slugs of a page are generated from its file path.
| path (relative to content folder) | slugs |
| --------------------------------- | ----------------- |
| `./dir/page.mdx` | `['dir', 'page']` |
| `./dir/index.mdx` | `['dir']` |
## Folder
Organize multiple pages, you can create a [Meta file](#meta) to customise folders.
### Folder Group
By default, putting a file into folder will change its slugs.
You can wrap the folder name in parentheses to avoid impacting the slugs of child files.
| path (relative to content folder) | slugs |
| --------------------------------- | ---------- |
| `./(group-name)/page.mdx` | `['page']` |
## Meta
Customise folders by creating a `meta.json` file in the folder.
```json title="meta.json"
{
"title": "Display Name",
"icon": "MyIcon",
"pages": ["index", "getting-started"],
"defaultOpen": true
}
```
| name | description |
| ------------- | ------------------------------------- |
| `title` | Display name |
| `icon` | The name of icon, see [Icons](#icons) |
| `pages` | Folder items (see below) |
| `defaultOpen` | Open the folder by default |
### Pages
By default, folder items are sorted alphabetically.
You can add or control the order of items using `pages`, items are not included unless they are listed inside.
```json title="meta.json"
{
"title": "Name of Folder",
"pages": ["guide", "components", "---My Separator---", "./nested/page"]
}
```
#### Rest
Add a `...` item to include remaining pages (sorted alphabetically), or `z...a` for descending order.
```json title="meta.json"
{
"pages": ["guide", "..."]
}
```
You can add `!name` to prevent an item from being included.
```json title="meta.json"
{
"pages": ["guide", "...", "!components"]
}
```
#### Extract
You can extract the items from a folder with `...folder_name`.
```json title="meta.json"
{
"pages": ["guide", "...nested"]
}
```
#### Link
Use the syntax `[Text](url)` to insert links, or `[Icon][Text](url)` to add icon.
```json title="meta.json"
{
"pages": [
"[Vercel](https://vercel.com)",
"[Triangle][Vercel](https://vercel.com)"
]
}
```
## Icons
Since Fumadocs doesn't include an icon library, you have to convert the icon names to JSX elements in runtime, and render it as a component.
You can add an [`icon` handler](/docs/headless/source-api#icons) to `loader()`.
## Root Folder
Marks the folder as a root folder, only items in the opened root folder will be considered.
```json title="meta.json"
{
"title": "Name of Folder",
"description": "The description of root folder (optional)",
"root": true
}
```
For example, when you are opening a root folder `framework`, the other folders (e.g. `headless`) are not shown on the sidebar and other navigation elements.
Fumadocs UI renders root folders as [Sidebar Tabs](/docs/ui/navigation/sidebar#sidebar-tabs), which allows user to switch between them.
## Internationalization
You can add Markdown/meta files for different languages by attending `.{locale}` to your file name, like `page.cn.md` and `meta.cn.json`.
Make sure to create a file for the default locale first, the locale code is optional (e.g. both `get-started.mdx` and `get-started.en.mdx` are accepted).
# Fumadocs Framework: Search
URL: /docs/ui/search
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/search.mdx
Implement document search in your docs
## Search UI
You can customise the search UI from root provider, such as disabling search:
```tsx title="app/layout.tsx"
import { RootProvider } from 'fumadocs-ui/provider';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
### Hot Keys
Customise the hot keys to trigger search dialog, by default it's ⌘K or CtrlK.
```tsx
import { RootProvider } from 'fumadocs-ui/provider';
{children}
;
```
### Replace Search Dialog
Make a `` wrapper with `use client` directive, and use it instead of your previous root provider.
```tsx tab="provider.tsx"
'use client';
import { RootProvider } from 'fumadocs-ui/provider';
import type { ReactNode } from 'react';
// for example, using the default `fetch()` client
import SearchDialog from 'fumadocs-ui/components/dialog/search-default';
export function Provider({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
```tsx tab="app/layout.tsx"
import { Provider } from './provider';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
## Search Server
The search server/backend is provided and documented on Fumadocs Core, see [available engines](/docs/headless/search).
## Search Client
You can choose a search client according to your search engine, it defaults to `fetch` client.
### `fetch`
It sends queries to an API endpoint, works with the built-in [Orama search engine](/docs/headless/search/orama).
You can pass options to the search client:
```tsx
import { RootProvider } from 'fumadocs-ui/provider';
{children}
;
```
#### Tag Filter
Add UI for filtering results by tags, [configure Tag Filter](/docs/headless/search/orama#tag-filter) on search server and add the following:
```tsx
import { RootProvider } from 'fumadocs-ui/provider';
{children}
;
```
### Algolia
For the setup guide, see [Integrate Algolia Search](/docs/headless/search/algolia).
While generally we recommend building your own search with their client-side
SDK, you can also plug the built-in dialog interface.
```tsx title="components/search.tsx"
'use client';
import algo from 'algoliasearch/lite';
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
import SearchDialog from 'fumadocs-ui/components/dialog/search-algolia';
const appId = process.env.NEXT_PUBLIC_ALGOLIA_APP_ID;
const apiKey = process.env.NEXT_PUBLIC_ALGOLIA_API_KEY;
const indexName = process.env.NEXT_PUBLIC_ALGOLIA_INDEX;
if (!appId || !apiKey || !indexName) throw new Error('Algolia credentials');
const client = algo(appId, apiKey);
const index = client.initIndex(indexName);
export default function CustomSearchDialog(props: SharedProps) {
return ;
}
```
1. Replace `appId`, `apiKey` and `indexName` with your desired values.
2. [Replace the default search dialog](#replace-search-dialog) with your new component.
The built-in implementation doesn't use instant search (their official
javascript client).
#### Tag Filter
Same as default search client, you can configure [Tag Filter](/docs/headless/search/algolia#tag-filter) on the dialog.
```tsx title="components/search.tsx"
import SearchDialog from 'fumadocs-ui/components/dialog/search-algolia';
;
```
### Orama Cloud
For the setup guide, see [Integrate Orama Cloud](/docs/headless/search/orama-cloud).
```tsx title="components/search.tsx"
'use client';
import { OramaClient } from '@oramacloud/client';
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
import SearchDialog from 'fumadocs-ui/components/dialog/search-orama';
const client = new OramaClient({
endpoint: 'endpoint',
api_key: 'apiKey',
});
export default function CustomSearchDialog(props: SharedProps) {
return ;
}
```
1. Replace `endpoint`, `apiKey` with your desired values.
2. [Replace the default search dialog](#replace-search-dialog) with your new component.
### Community Integrations
A list of integrations maintained by community.
* [Trieve Search](/docs/headless/search/trieve)
## Built-in UI
If you want to use the built-in search dialog UI instead of building your own,
you may use the `SearchDialog` component.
```tsx
import {
SearchDialog,
type SharedProps,
} from 'fumadocs-ui/components/dialog/search';
export default function CustomSearchDialog(props: SharedProps) {
return ;
}
```
It is an internal API, might break during iterations
# Fumadocs Framework: Static Export
URL: /docs/ui/static-export
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/static-export.mdx
Enable static export with Fumadocs
## Overview
Fumadocs is fully compatible with Next.js static export, allowing you to export the app as a static HTML site without a Node.js server.
```js title="next.config.mjs"
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
output: 'export',
// Optional: Change links `/me` -> `/me/` and emit `/me.html` -> `/me/index.html`
// trailingSlash: true,
// Optional: Prevent automatic `/me` -> `/me/`, instead preserve `href`
// skipTrailingSlashRedirect: true,
};
```
See [Next.js docs](https://nextjs.org/docs/app/guides/static-exports) for limitations and details.
## Search
### Cloud Solutions
Since the search functionality is powered by remote servers, static export works without configuration.
### Built-in Search
You need to:
1. Build the search indexes statically using [`staticGET`](/docs/headless/search/orama#static-export).
2. Enable static mode on search client from Root Provider:
```tsx title="app/layout.tsx"
import { RootProvider } from 'fumadocs-ui/provider';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
This allows the route handler to be statically cached into a single file, and search will be computed on browser instead.
# Fumadocs Framework: Themes
URL: /docs/ui/theme
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/theme.mdx
Add Theme to Fumadocs UI
## Usage
Only Tailwind CSS v4 is supported, the preset will also include source to Fumadocs UI itself:
```css title="Tailwind CSS"
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
```
### Preflight Changes
By using the Tailwind CSS plugin, or the pre-built stylesheet, your default border, text and background
colors will be changed.
### Light/Dark Modes
Fumadocs supports light/dark modes with [`next-themes`](https://github.com/pacocoursey/next-themes), it is included in Root Provider.
See [Root Provider](/docs/ui/layouts/root-provider#theme-provider) to learn more.
### RTL Layout
RTL (Right-to-left) layout is supported.
To enable RTL, set the `dir` prop to `rtl` in body and root provider (required for Radix UI).
```tsx
import { RootProvider } from 'fumadocs-ui/provider';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
### Layout Width
Customise the max width of docs layout with CSS Variables.
```css
:root {
--fd-layout-width: 1400px;
}
```
## Tailwind CSS Preset
Fumadocs UI adds its own colors, animations, and utilities with Tailwind CSS preset.
### Colors
It comes with many themes out-of-the-box, you can pick one you prefer.
```css
@import 'fumadocs-ui/css/.css';
@import 'fumadocs-ui/css/preset.css';
```







The design system was inspired by [Shadcn UI](https://ui.shadcn.com), you can also customize the colors using CSS variables.
```css title="global.css"
:root {
--color-fd-background: hsl(0, 0%, 100%);
}
.dark {
--color-fd-background: hsl(0, 0%, 0%);
}
```
For Shadcn UI, you can use the `shadcn` preset instead.
It uses colors from your Shadcn UI theme.
```css
@import 'tailwindcss';
@import 'fumadocs-ui/css/shadcn.css';
@import 'fumadocs-ui/css/preset.css';
```
### Typography
We have a built-in plugin forked from [Tailwind CSS Typography](https://tailwindcss.com/docs/typography-plugin).
The plugin adds a `prose` class and variants to customise it.
```tsx
Good Heading
```
> The plugin works with and only with Fumadocs UI's MDX components, it may conflict with `@tailwindcss/typography`.
> If you need to use `@tailwindcss/typography` over the default plugin, [set a class name option](https://github.com/tailwindlabs/tailwindcss-typography/blob/main/README.md#changing-the-default-class-name) to avoid conflicts.
# Fumadocs Framework: Versioning
URL: /docs/ui/versioning
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/versioning.mdx
Implementing multi-version in your docs.
## Overview
It's common for developer tool related docs to version their docs, such as different docs for v1 and v2 of the same tool.
Fumadocs provide the primitives for you to implement versioning on your own way.
## Partial Versioning
When versioning only applies to part of your docs, You can separate them by folders.
For example:
You may want to group them with tabs rather than folders [using Sidebar
Tabs](/docs/ui/navigation/sidebar#sidebar-tabs).
## Full Versioning
Sometimes you want to version the entire website, such as [https://v14.fumadocs.dev](https://v14.fumadocs.dev) (Fumadocs v14) and [https://fumadocs.dev](https://fumadocs.dev) (Latest Fumadocs).
You can create a Git branch for a version of docs (call it `v2` for example), and deploy it as a separate app on another subdomain like `v2.my-site.com`.
Optionally, you can link to the other versions from your docs.
This design allows some advantages over partial versioning:
* Easy maintenance: Old docs/branches won't be affected when you iterate or upgrade dependencies.
* Better consistency: Not just the docs itself, your landing page (and other pages) will also be versioned.
# Fumadocs Framework: What is Fumadocs
URL: /docs/ui/what-is-fumadocs
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/what-is-fumadocs.mdx
Introducing Fumadocs, a docs framework that you can break.
Fumadocs was created because I wanted a more customisable experience for building docs, to be a docs framework that is not opinionated, **a "framework" that you can break**.
## Philosophy
**Less Abstraction:** Fumadocs expects you to write code and cooperate with the rest of your software.
While most frameworks are configured with a configuration file, they usually lack flexibility when you hope to tune its details.
You can’t control how they render the page nor the internal logic. Fumadocs shows you how the app works, instead of a single configuration file.
**Next.js Fundamentals:** It gives you the utilities and a good-looking UI.
You are still using features of Next.js App Router, like **Static Site Generation**. There is nothing new for Next.js developers, so you can use it with confidence.
**Opinionated on UI:** The only thing Fumadocs UI (the default theme) offers is **User Interface**. The UI is opinionated for bringing better mobile responsiveness and user experience.
Instead, we use a much more flexible approach inspired by Shadcn UI — [Fumadocs CLI](/docs/cli), so we can iterate our design quick, and welcome for more feedback about the UI.
## Why Fumadocs
Fumadocs is designed with flexibility in mind.
You can use `fumadocs-core` as a headless UI library and bring your own styles.
Fumadocs MDX is also a useful library to handle MDX content in Next.js. It also includes:
* Many built-in components.
* Typescript Twoslash, OpenAPI, and Math (KaTeX) integrations.
* Fast and optimized by default, natively built on App Router.
* Tight integration with Next.js, you can add it to an existing Next.js project easily.
You can read [Comparisons](/docs/ui/comparisons) if you're interested.
### Documentation
Fumadocs focuses on **authoring experience**, it provides a beautiful theme and many docs automation tools.
It helps you to iterate your codebase faster while never leaving your docs behind.
You can take this site as an example of docs site built with Fumadocs.
### Blog sites
Since Next.js is already a powerful framework, most features can be implemented with **just Next.js**.
Fumadocs provides additional tooling for Next.js, including syntax highlighting, document search, and a default theme (Fumadocs UI).
It helps you to avoid reinventing the wheels.
## When to use Fumadocs
For most of the web applications, vanilla React.js is no longer enough.
Nowadays, we also wish to have a blog, a showcase page, a FAQ page, etc. With a
fancy UI that's breathtaking, in these cases, Fumadocs can help you build the
docs easier, with less boilerplate.
Fumadocs is maintained by Fuma and many contributors, with care on the maintainability of codebase.
While we don't aim to offer every functionality people wanted, we're more focused on making basic features perfect and well-maintained.
You can also help Fumadocs to be more useful by contributing!
# Fumadocs Core (core library of framework): Breadcrumb
URL: /docs/headless/components/breadcrumb
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/headless/components/breadcrumb.mdx
The navigation component at the top of screen
A hook for implementing Breadcrumb in your documentation, it returns the path to
a page based on the given page tree.
> If present, the index page of a folder will be used as the item
## Usage
it exports a `useBreadcrumb` hook:
```ts twoslash
declare const tree: any;
// ---cut---
import { usePathname } from 'next/navigation';
import { useBreadcrumb } from 'fumadocs-core/breadcrumb';
const pathname = usePathname();
const items = useBreadcrumb(pathname, tree);
// ^?
```
### Example
A styled example.
```tsx
'use client';
import { usePathname } from 'next/navigation';
import { useBreadcrumb } from 'fumadocs-core/breadcrumb';
import type { PageTree } from 'fumadocs-core/server';
import { Fragment } from 'react';
import { ChevronRight } from 'lucide-react';
import Link from 'next/link';
export function Breadcrumb({ tree }: { tree: PageTree.Root }) {
const pathname = usePathname();
const items = useBreadcrumb(pathname, tree);
if (items.length === 0) return null;
return (
;
```
### Edit on GitHub
You can also add your own component.
```tsx
import { DocsBody } from 'fumadocs-ui/page';
Edit on GitHub
;
```
## Configurations
### Full Mode
To extend the page to fill up all available space, pass `full` to the page component.
This will force TOC to be shown as a popover.
```tsx
import { DocsPage } from 'fumadocs-ui/page';
...;
```
### Table of Contents
An overview of all the headings in your article, it requires an array of headings.
For Markdown and MDX documents, You can obtain it using the
[TOC Utility](/docs/headless/utils/get-toc). Content sources like Fumadocs MDX offer this out-of-the-box.
```tsx
import { DocsPage } from 'fumadocs-ui/page';
...;
```
You can customise or disable it with the `tableOfContent` option, or with `tableOfContentPopover` on smaller devices.
```tsx
import { DocsPage } from 'fumadocs-ui/page';
...
;
```
#### Style
You can choose another style for TOC, like `clerk` inspired by [https://clerk.com](https://clerk.com):
```tsx
import { DocsPage } from 'fumadocs-ui/page';
...
;
```
### Last Updated Time
Display last updated time of the page.
```tsx
import { DocsPage } from 'fumadocs-ui/page';
;
```
Since you might have different version controls (e.g. Github) or it's from
remote sources like Sanity, Fumadocs UI doesn't display the last updated time by
default.
For Github hosted documents, you can use
the [`getGithubLastEdit`](/docs/headless/utils/git-last-edit) utility.
```tsx
import { DocsPage } from 'fumadocs-ui/page';
import { getGithubLastEdit } from 'fumadocs-core/server';
const time = await getGithubLastEdit({
owner: 'fuma-nama',
repo: 'fumadocs',
path: `content/docs/${page.file.path}`,
});
;
```
Or you may specify the updated time in [frontmatter](/docs/mdx/collections#schema).
### Footer
Footer is a navigation element that has two buttons to jump to the next and previous pages. When not specified, it shows the neighbour pages found from page tree.
Customise the footer with the `footer` option.
```tsx
import { DocsPage, DocsBody } from 'fumadocs-ui/page';
...;
```
### Breadcrumb
A navigation element, shown only when user is navigating in folders.
# Fumadocs Framework: Root Provider
URL: /docs/ui/layouts/root-provider
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/layouts/root-provider.mdx
The context provider of Fumadocs UI.
The context provider of all the components, including `next-themes` and context
for search dialog. It should be located at the root layout.
## Usage
```jsx
import { RootProvider } from 'fumadocs-ui/provider';
export default function Layout({ children }) {
return (
{children}
);
}
```
### Search Dialog
Customize or disable the search dialog with `search` option.
```jsx
{children}
```
Learn more from [Search](/docs/ui/search).
### Theme Provider
Fumadocs supports light/dark modes with [`next-themes`](https://github.com/pacocoursey/next-themes).
Customise or disable it with `theme` option.
```jsx
{children}
```
# Fumadocs Framework: Markdown
URL: /docs/ui/markdown
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/markdown/index.mdx
How to write documents
## Introduction
Fumadocs provides many useful extensions to MDX, a markup language. Here is a brief introduction to the default MDX syntax of Fumadocs.
> MDX is not the only supported format of Fumadocs. In fact, you can use any renderers such as `next-mdx-remote` or CMS.
## MDX
We recommend MDX, a superset of Markdown with JSX syntax.
It allows you to import components, and use them right in the document, or even export values.
See:
* [MDX Syntax](https://mdxjs.com/docs/what-is-mdx/#mdx-syntax).
* GFM (GitHub Flavored Markdown) is also supported, see [GFM Specification](https://github.github.com/gfm).
```mdx
---
title: This is a document
---
import { Component } from './component';
# Heading
## Heading
### Heading
#### Heading
Hello World, **Bold**, _Italic_, ~~Hidden~~
1. First
2. Second
3. Third
- Item 1
- Item 2
> Quote here

| Table | Description |
| ----- | ----------- |
| Hello | World |
```
### Images
Images are automatically optimized for `next/image`.
```mdx

```
### Auto Links
Internal links use the `next/link` component to allow prefetching and avoid hard-reload.
External links will get the default `rel="noreferrer noopener" target="_blank"` attributes for security.
```mdx
[My Link](https://github.github.com/gfm)
This also works: https://github.github.com/gfm.
```
### Cards
Useful for adding links.
```mdx
import { HomeIcon } from 'lucide-react';
Learn more about caching in Next.js
Learn more about `fetch` in Next.js.} href="/" title="Home">
You can include icons too.
```
Learn more about caching in Next.js
Learn more about `fetch` in Next.js.} href="/" title="Home">
You can include icons too.
#### "Further Reading" Section
You can do something like:
```tsx title="page.tsx"
import { getPageTreePeers } from 'fumadocs-core/server';
import { source } from '@/lib/source';
{getPageTreePeers(source.pageTree, '/docs/my-page').map((peer) => (
{peer.description}
))}
;
```
This will show the other pages in the same folder as cards.
### Callouts
Useful for adding tips/warnings, it is included by default. You can specify the type of callout:
* `info` (default)
* `warn`
* `error`
```mdx
Hello WorldHello World
Hello World
```
Hello WorldHello World
Hello World
### Headings
An anchor is automatically applied to each heading, it sanitizes invalid characters like spaces. (e.g. `Hello World` to `hello-world`)
```md
# Hello `World`
```
#### TOC Settings
The table of contents (TOC) will be generated based on headings, you can also customise the effects of headings:
```md
# Heading [!toc]
This heading will be hidden from TOC.
# Another Heading [toc]
This heading will **only** be visible in TOC, you can use it to add additional TOC items.
Like headings rendered in a React component:
```
#### Custom Anchor
You can add `[#slug]` to customise heading anchors.
```md
# heading [#my-heading-id]
```
You can also chain it with TOC settings like:
```md
# heading [toc] [#my-heading-id]
```
To link people to a specific heading, add the heading id to hash fragment: `/page#my-heading-id`.
### Codeblock
Syntax Highlighting is supported by default using [Rehype Code](/docs/headless/mdx/rehype-code).
````mdx
```js
console.log('Hello World');
```
````
You can add a title to the codeblock.
````mdx
```js title="My Title"
console.log('Hello World');
```
````
#### Shiki Transformers
We support some of the [Shiki Transformers](https://shiki.style/packages/transformers), allowing you to highlight/style specific lines.
````md tab="Input"
```tsx
// highlight a line
Hello World
// [\!code highlight]
// highlight a word
// [\!code word:Fumadocs]
Fumadocs
// diff styles
console.log('hewwo'); // [\!code --]
console.log('hello'); // [\!code ++]
```
````
```tsx tab="Output"
// highlight a line
Hello World
// [!code highlight]
// highlight a word
// [!code word:Fumadocs]
Fumadocs
// diff styles:
console.log('hewwo'); // [!code --]
console.log('hello'); // [!code ++]
```
#### Tab Groups
You can use code blocks with the `` component.
````mdx
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
```ts tab="Tab 1"
console.log('A');
```
```ts tab="Tab 2"
console.log('B');
```
````
> Note that you can add MDX components instead of importing them in MDX files.
```ts tab="Tab 1"
console.log('A');
```
```ts tab="Tab 2"
console.log('B');
```
### Include
> This is only available on **Fumadocs MDX**.
Reference another file (can also be a Markdown/MDX document).
Specify the target file path in `` tag (relative to the MDX file itself).
```mdx title="page.mdx"
./another.mdx
```
See [other usages](/docs/mdx/include).
## Additional Features
You may be interested:
### Package Install
Generate commands for installing packages via package managers.
````md tab="Input"
```package-install
npm i next -D
```
````
```bash tab="Output"
npm i next -D
```
```bash tab="Output"
pnpm add next -D
```
```bash tab="Output"
yarn add next --dev
```
```bash tab="Output"
bun add next --dev
```
To enable, see [Remark Install](/docs/headless/mdx/install).
# Fumadocs Framework: Math
URL: /docs/ui/markdown/math
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/markdown/math.mdx
Writing math equations in Markdown/MDX.
## Getting Started
```bash
npm install remark-math rehype-katex katex
```
```bash
pnpm add remark-math rehype-katex katex
```
```bash
yarn add remark-math rehype-katex katex
```
```bash
bun add remark-math rehype-katex katex
```
### Add Plugins
Add the required remark/rehype plugins, the code might be vary depending on your content source.
```ts title="source.config.ts" tab="Fumadocs MDX"
import rehypeKatex from 'rehype-katex';
import remarkMath from 'remark-math';
import { defineConfig } from 'fumadocs-mdx/config';
export default defineConfig({
mdxOptions: {
remarkPlugins: [remarkMath],
// Place it at first, it should be executed before the syntax highlighter
rehypePlugins: (v) => [rehypeKatex, ...v],
},
});
```
### Add Stylesheet
Add the following to root layout to make it looks great:
```tsx title="layout.tsx"
import 'katex/dist/katex.css';
```
### Done
Type some TeX expression in your documents, like the Pythagoras theorem:
````mdx
Inline: $$c = \pm\sqrt{a^2 + b^2}$$
```math
c = \pm\sqrt{a^2 + b^2}
```
````
Inline: $$c = \pm\sqrt{a^2 + b^2}$$
```math
c = \pm\sqrt{a^2 + b^2}
```
Taylor Expansion (expressing holomorphic function $$f(x)$$ in power series):
```math
\displaystyle {\begin{aligned}T_{f}(z)&=\sum _{k=0}^{\infty }{\frac {(z-c)^{k}}{2\pi i}}\int _{\gamma }{\frac {f(w)}{(w-c)^{k+1}}}\,dw\\&={\frac {1}{2\pi i}}\int _{\gamma }{\frac {f(w)}{w-c}}\sum _{k=0}^{\infty }\left({\frac {z-c}{w-c}}\right)^{k}\,dw\\&={\frac {1}{2\pi i}}\int _{\gamma }{\frac {f(w)}{w-c}}\left({\frac {1}{1-{\frac {z-c}{w-c}}}}\right)\,dw\\&={\frac {1}{2\pi i}}\int _{\gamma }{\frac {f(w)}{w-z}}\,dw=f(z),\end{aligned}}
```
You can actually copy equations on Wikipedia, they will be converted into a KaTeX string when you paste it.
```math
\displaystyle S[{\boldsymbol {q}}]=\int _{a}^{b}L(t,{\boldsymbol {q}}(t),{\dot {\boldsymbol {q}}}(t))\,dt.
```
# Fumadocs Framework: Mermaid
URL: /docs/ui/markdown/mermaid
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/markdown/mermaid.mdx
Rendering diagrams in your docs
Fumadocs doesn't have a built-in Mermaid wrapper provided, we recommend using `mermaid` directly.
## Setup
Install the required dependencies, `next-themes` is used with Fumadocs to manage the light/dark mode.
```bash
npm install mermaid next-themes
```
```bash
pnpm add mermaid next-themes
```
```bash
yarn add mermaid next-themes
```
```bash
bun add mermaid next-themes
```
Create the Mermaid component:
```tsx title="components/mdx/mermaid.tsx"
'use client';
import { useEffect, useId, useRef, useState } from 'react';
import type { MermaidConfig } from 'mermaid';
import { useTheme } from 'next-themes';
export function Mermaid({ chart }: { chart: string }) {
const id = useId();
const [svg, setSvg] = useState('');
const containerRef = useRef(null!);
const { resolvedTheme } = useTheme();
useEffect(() => {
void renderChart();
async function renderChart() {
const mermaidConfig: MermaidConfig = {
startOnLoad: false,
securityLevel: 'loose',
fontFamily: 'inherit',
themeCSS: 'margin: 1.5rem auto 0;',
theme: resolvedTheme === 'dark' ? 'dark' : 'default',
};
const { default: mermaid } = await import('mermaid');
try {
mermaid.initialize(mermaidConfig);
const { svg } = await mermaid.render(
// strip invalid characters for `id` attribute
id.replaceAll(':', ''),
chart.replaceAll('\\n', '\n'),
containerRef.current,
);
setSvg(svg);
} catch (error) {
console.error('Error while rendering mermaid', error);
}
}
}, [chart, id, resolvedTheme]);
return ;
}
```
> This is originally inspired by [remark-mermaid](https://github.com/the-guild-org/docs/blob/main/packages/remark-mermaid/src/mermaid.tsx).
Add the component as a MDX component:
```tsx title="mdx-components.tsx"
import defaultMdxComponents from 'fumadocs-ui/mdx';
import { Mermaid } from '@/components/mdx/mermaid';
import type { MDXComponents } from 'mdx/types';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultMdxComponents,
Mermaid,
...components,
};
}
```
## Usage
Use it in MDX files.
```mdx
```
# Fumadocs Framework: Twoslash
URL: /docs/ui/markdown/twoslash
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/markdown/twoslash.mdx
Use Typescript Twoslash in your docs
## Usage
Thanks to the Twoslash integration of [Shiki](https://github.com/shikijs/shiki), the default code syntax highlighter, it is as simple as adding a transformer.
```bash
npm install fumadocs-twoslash twoslash
```
```bash
pnpm add fumadocs-twoslash twoslash
```
```bash
yarn add fumadocs-twoslash twoslash
```
```bash
bun add fumadocs-twoslash twoslash
```
Update your `serverExternalPackages` in Next.js config:
```js
import { createMDX } from 'fumadocs-mdx/next';
const config = {
reactStrictMode: true,
serverExternalPackages: ['typescript', 'twoslash'],
};
const withMDX = createMDX();
export default withMDX(config);
```
Add to your Shiki transformers.
```ts twoslash title="source.config.ts (Fumadocs MDX)"
import { defineConfig } from 'fumadocs-mdx/config';
import { transformerTwoslash } from 'fumadocs-twoslash';
import { rehypeCodeDefaultOptions } from 'fumadocs-core/mdx-plugins';
export default defineConfig({
mdxOptions: {
rehypeCodeOptions: {
themes: {
light: 'github-light',
dark: 'github-dark',
},
transformers: [
...(rehypeCodeDefaultOptions.transformers ?? []),
transformerTwoslash(),
],
},
},
});
```
Add styles, Tailwind CSS v4 is required.
```css title="Tailwind CSS"
@import 'fumadocs-twoslash/twoslash.css';
```
Add MDX components.
```tsx title="mdx-components.tsx"
import * as Twoslash from 'fumadocs-twoslash/ui';
import defaultComponents from 'fumadocs-ui/mdx';
import type { MDXComponents } from 'mdx/types';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultComponents,
...Twoslash,
...components,
};
}
```
Now you can add `twoslash` meta string to codeblocks.
````md
```ts twoslash
console.log('Hello World');
```
````
### Example
Learn more about [Twoslash notations](https://twoslash.netlify.app/refs/notations).
```ts twoslash title="Test"
type Player = {
/**
* The player name
* @default 'user'
*/
name: string;
};
// ---cut---
// @noErrors
console.g;
// ^|
const player: Player = { name: 'Hello World' };
// ^?
```
```ts twoslash
const a = '123';
console.log(a);
// ^^^
```
```ts twoslash
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
input: ['./museum.yaml'],
output: './content/docs/ui',
});
```
```ts twoslash
// @errors: 2588
const a = '123';
a = 132;
```
## Cache
You can enable filesystem cache with `typesCache` option:
```ts twoslash title="source.config.ts"
import { transformerTwoslash } from 'fumadocs-twoslash';
import { createFileSystemTypesCache } from 'fumadocs-twoslash/cache-fs';
transformerTwoslash({
typesCache: createFileSystemTypesCache(),
});
```
# Fumadocs Framework: Code Block
URL: /docs/ui/mdx/codeblock
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/mdx/codeblock.mdx
Displaying Shiki highlighted code blocks
This is a MDX component to be used with [Rehype Code](/docs/headless/mdx/rehype-code) to display highlighted codeblocks.
Supported features:
* Copy button
* Custom titles and icons
> If you're looking for an equivalent with runtime syntax highlighting, see [Dynamic Code Block](/docs/ui/components/dynamic-codeblock).
## Usage
Wrap the pre element in ``, which acts as the wrapper of code block.
```tsx title="mdx-components.tsx"
import defaultComponents from 'fumadocs-ui/mdx';
import type { MDXComponents } from 'mdx/types';
import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultComponents,
// HTML `ref` attribute conflicts with `forwardRef`
pre: ({ ref: _ref, ...props }) => (
{props.children}
{/* [!code highlight] */}
),
...components,
};
}
```
See [Markdown](/docs/ui/markdown#codeblock) for usages.
### Keep Background
Use the background color generated by Shiki.
```tsx
import { Pre, CodeBlock } from 'fumadocs-ui/components/codeblock';
{props.children}
;
```
### Icons
Specify a custom icon by passing an `icon` prop to `CodeBlock` component.
By default, the icon will be injected by the custom Shiki transformer.
```js title="config.js"
console.log('js');
```
# Fumadocs Framework: MDX
URL: /docs/ui/mdx
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/mdx/index.mdx
Default MDX Components
## Usage
The default MDX components include Cards, Callouts, Code Blocks and Headings.
```ts
import defaultMdxComponents from 'fumadocs-ui/mdx';
```
### Relative Link
To support links with relative file path in `href`, override the default `a` component with:
```tsx title="app/docs/[[...slug]]/page.tsx"
import { createRelativeLink } from 'fumadocs-ui/mdx';
import { source } from '@/lib/source';
import { getMDXComponents } from '@/mdx-components';
const page = source.getPage(['...']);
return (
);
```
```mdx
[My Link](./file.mdx)
```
[Example: `../(integrations)/open-graph.mdx`](../\(integrations\)/open-graph.mdx)
Server Component only.
# Fumadocs Framework: Navigation
URL: /docs/ui/navigation
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/navigation/index.mdx
Configure navigation in your Fumadocs app.
# Fumadocs Framework: Layout Links
URL: /docs/ui/navigation/links
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/navigation/links.mdx
Customise the shared navigation links on all layouts.
## Overview
Fumadocs allows adding additional links to your layouts with a `links` prop, like linking to your "showcase" page.
```tsx tab="Shared Options" title="app/layout.config.tsx"
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export const baseOptions: BaseLayoutProps = {
links: [], // [!code highlight]
// other options
};
```
```tsx tab="Docs Layout"
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { baseOptions } from '@/app/layout.config';
import { source } from '@/lib/source';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
```tsx tab="Home Layout"
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions } from '@/app/layout.config';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
You can see all supported items below:
### Link Item
A link to navigate to a URL/href, can be external.
```tsx title="app/layout.config.tsx"
import { BookIcon } from 'lucide-react';
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export const baseOptions: BaseLayoutProps = {
links: [
{
icon: ,
text: 'Blog',
url: '/blog',
// secondary items will be displayed differently on navbar
secondary: false,
},
],
};
```
#### Active Mode
The conditions to be marked as active.
| Mode | Description |
| ------------ | ----------------------------------------------------------- |
| `url` | When browsing the specified url |
| `nested-url` | When browsing the url and its child pages like `/blog/post` |
| `none` | Never be active |
```tsx title="app/layout.config.tsx"
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export const baseOptions: BaseLayoutProps = {
links: [
{
text: 'Blog',
url: '/blog',
active: 'nested-url',
},
],
};
```
### Icon Item
Same as link item, but is shown as an icon button.
Icon items are secondary by default.
```tsx title="app/layout.config.tsx"
import { BookIcon } from 'lucide-react';
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export const baseOptions: BaseLayoutProps = {
links: [
{
type: 'icon',
label: 'Visit Blog', // `aria-label`
icon: ,
text: 'Blog',
url: '/blog',
},
],
};
```
### Navigation Menu
A navigation menu containing link items.
```tsx title="app/layout.config.tsx"
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export const baseOptions: BaseLayoutProps = {
links: [
{
type: 'menu',
text: 'Guide',
items: [
{
text: 'Getting Started',
description: 'Learn to use Fumadocs',
url: '/docs',
// (optional) Props for Radix UI Navigation Menu item in Home Layout
menu: {
className: 'row-span-2',
// add banner to navigation menu card
// can be an image or other elements
banner:
Banner
,
},
},
],
},
],
};
```
Note that the `description` field will only be displayed on the navbar in Home Layout.
### Custom Item
Display a custom component.
```tsx title="app/layout.config.tsx"
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export const baseOptions: BaseLayoutProps = {
links: [
{
type: 'custom',
children: ,
secondary: true,
},
],
};
```
### GitHub URL
There's also a shortcut for adding GitHub repository link item.
```tsx twoslash title="app/layout.config.tsx"
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export const baseOptions: BaseLayoutProps = {
githubUrl: 'https://github.com',
};
```
# Fumadocs Framework: Sidebar Links
URL: /docs/ui/navigation/sidebar
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/navigation/sidebar.mdx
Customise sidebar navigation links on Docs Layout.
## Overview

Sidebar items are rendered from the page tree you passed to ``.
For `source.pageTree`, it generates the tree from your file structure, you can see [Routing](/docs/ui/page-conventions) for available patterns.
```tsx title="layout.tsx"
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { source } from '@/lib/source';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
You may hardcode it too:
```tsx title="layout.tsx"
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
{children}
);
}
```
### Sidebar Tabs
Tabs are folders with tab-like behaviours, only the content of opened tab will be visible.
Fumadocs provide a dropdown component to switch between tabs, it will be hidden unless one of its items is active.

You can add items by marking folders as [Root Folders](/docs/ui/page-conventions#root-folder), create a `meta.json` file in the folder:
```json title="content/docs/my-folder/meta.json"
{
"title": "Name of Folder",
"description": "The description of root folder (optional)",
"root": true
}
```
Or specify them explicitly:
```tsx title="/app/docs/layout.tsx"
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
;
```
Set it to `false` to disable:
```tsx
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
;
```
You can specify a `banner` to the [Docs Layout](/docs/ui/layouts/docs) component.
```tsx
import { DocsLayout, type DocsLayoutProps } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions } from '@/app/layout.config';
import { source } from '@/lib/source';
const docsOptions: DocsLayoutProps = {
...baseOptions,
tree: source.pageTree,
sidebar: {
banner:
Hello World
,
},
};
export default function Layout({ children }: { children: ReactNode }) {
return {children};
}
```
#### Decoration
Change the icon/styles of tabs.
```tsx
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
({
...option,
icon: ,
}),
},
}}
/>;
```
# Fumadocs Framework: Configurations
URL: /docs/ui/openapi/configurations
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/(integrations)/openapi/configurations.mdx
Customise Fumadocs OpenAPI
## File Generator
Pass options to the `generateFiles` function.
### Input
An array of input files.
Allowed:
* File Paths
* External URLs
* Wildcard
```ts
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
input: ['./unkey.json'],
});
```
On Next.js server, the schema is dynamically fetched when the `APIPage` component renders.
If the schema is passed as a file path, ensure the page **will not** be re-rendered after build.
### Output
Path to the output directory.
```ts
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
output: '/content/docs',
});
```
### Per
Customise how the page is generated, default to `operation`.
| mode | Generate a page for |
| --------- | ----------------------------------- |
| tag | each tag |
| file | each schema |
| operation | each operation (method of endpoint) |
```ts
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
per: 'tag',
});
```
### Group By
In `operation` mode, you can group output files with folders.
| Group by | Description |
| -------- | ------------------------------------------------------------ |
| tag | `{tag}/{page}.mdx` (Each operation can only contain `1` tag) |
| route | `{api-endpoint}/{page}.mdx` |
| none | `{page}.mdx` (default) |
```ts
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
per: 'operation',
groupBy: 'tag',
});
```
### Name
A function that controls the output path of files.
```ts
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
name: (type, file) => {
return; // filename
},
});
```
### Imports
Add additional imports on the top of MDX files.
```ts
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
imports: [
{
names: ['Component1', 'Component2'],
from: '@/components/ui/api',
},
],
});
```
### Frontmatter
Customise the frontmatter of MDX files.
By default, it includes:
| property | description |
| ------------- | ------------------------------------------------ |
| `title` | Page title |
| `description` | Page description |
| `full` | Always true, added for Fumadocs UI |
| `method` | Available method of operation (`operation` mode) |
| `route` | Route of operation (`operation` mode) |
```ts
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
input: ['./petstore.yaml'],
output: './content/docs',
frontmatter: (title, description) => ({
myProperty: 'hello',
}),
});
```
### Add Generated Comment
Add a comment to the top of generated files indicating they are auto-generated.
```ts
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
input: ['./petstore.yaml'],
output: './content/docs',
// Add default comment
addGeneratedComment: true,
// Or provide a custom comment
addGeneratedComment: 'Custom auto-generated comment',
// Or disable comments
addGeneratedComment: false,
});
```
### Tag Display Name
Adding `x-displayName` to OpenAPI Schema can control the display name of your tags.
```yaml title="openapi.yaml"
tags:
- name: test
description: this is a tag.
x-displayName: My Test Name
```
## OpenAPI Server
The server to render pages.
### Generate Code Samples
Generate custom code samples for each API endpoint. Make sure to install the types package to give you type-safety when customising it:
```bash
npm install openapi-types -D
```
```bash
pnpm add openapi-types -D
```
```bash
yarn add openapi-types --dev
```
```bash
bun add openapi-types --dev
```
```ts
import { createOpenAPI } from 'fumadocs-openapi/server';
export const openapi = createOpenAPI({
generateCodeSamples(endpoint) {
return [
{
lang: 'js',
label: 'JavaScript SDK',
source: "console.log('hello')",
},
];
},
});
```
In addition, you can also specify code samples via OpenAPI schema.
```yaml
paths:
/plants:
get:
x-codeSamples:
- lang: js
label: JavaScript SDK
source: |
const planter = require('planter');
planter.list({ unwatered: true });
```
#### Disable Code Sample
You can disable the code sample for a specific language, for example, to disable cURL:
```ts
import { createOpenAPI } from 'fumadocs-openapi/server';
export const openapi = createOpenAPI({
generateCodeSamples(endpoint) {
return [
{
lang: 'curl',
label: 'cURL',
source: false,
},
];
},
});
```
### Renderer
Customise components in the page.
```ts
import { createOpenAPI } from 'fumadocs-openapi/server';
export const openapi = createOpenAPI({
renderer: {
Root(props) {
// your own (server) component
},
},
});
```
## Advanced
### Using API Page
> This is not a public API, use it carefully.
To use the `APIPage` component in your MDX files:
```mdx
---
title: Delete Api
full: true
---
```
| Prop | Description |
| ------------ | ----------------------------------------- |
| `document` | OpenAPI Schema |
| `operations` | Operations (API endpoints) to be rendered |
| `hasHead` | Enable to render the heading of operation |
# Fumadocs Framework: OpenAPI
URL: /docs/ui/openapi
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/(integrations)/openapi/index.mdx
Generating docs for OpenAPI schema
## Manual Setup
Install the required packages.
```bash
npm install fumadocs-openapi shiki
```
```bash
pnpm add fumadocs-openapi shiki
```
```bash
yarn add fumadocs-openapi shiki
```
```bash
bun add fumadocs-openapi shiki
```
### Generate Styles
Please note that you must have Tailwind CSS v4 configured.
```css title="Tailwind CSS"
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
/* [!code ++] */
@import 'fumadocs-openapi/css/preset.css';
```
### Configure Pages
Create an OpenAPI instance on the server. Fumadocs OpenAPI renders the pages on server-side.
```ts title="lib/source.ts"
import { createOpenAPI, attachFile } from 'fumadocs-openapi/server';
import { loader } from 'fumadocs-core/source';
export const source = loader({
pageTree: {
// [!code ++] adds a badge to each page item in page tree
attachFile,
},
});
export const openapi = createOpenAPI({
// options
});
```
Add `APIPage` to your MDX Components, so that you can use it in MDX files.
```tsx title="mdx-components.tsx"
import defaultComponents from 'fumadocs-ui/mdx';
import { APIPage } from 'fumadocs-openapi/ui';
import { openapi } from '@/lib/source';
import type { MDXComponents } from 'mdx/types';
export function getMDXComponents(components?: MDXComponents): MDXComponents {
return {
...defaultComponents,
APIPage: (props) => ,
...components,
};
}
```
> It is a React Server Component.
### Generate Files
You can generate MDX files directly from your OpenAPI schema.
Create a script:
```js title="scripts/generate-docs.mjs"
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
// the OpenAPI schema
// For Vercel users, we recommend a URL instead.
input: ['./unkey.json'],
output: './content/docs',
// we recommend to enable it
// make sure your endpoint description doesn't break MDX syntax.
includeDescription: true,
});
```
> Only OpenAPI 3.0 and 3.1 are supported.
Generate docs with the script:
```bash
node ./scripts/generate-docs.mjs
```
## Features
The official OpenAPI integration supports:
* Basic API endpoint information
* Interactive API playground
* Example code to send request (in different programming languages)
* Response samples and TypeScript definitions
* Request parameters and body generated from schemas
### Demo
[View demo](/docs/openapi).
# Fumadocs Framework: Creating Proxy
URL: /docs/ui/openapi/proxy
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/ui/(integrations)/openapi/proxy.mdx
Avoid CORS problem
## Introduction
A proxy server is useful for executing HTTP (`fetch`) requests, as it doesn't have CORS constraints like on the browser.
We can use it for executing HTTP requests on the OpenAPI playground, when the target API endpoints do not have CORS configured correctly.
Do not use this on unreliable sites and API endpoints, the proxy server will
forward all received headers & body, including HTTP-only `Cookies` and
`Authorization` header.
### Setup
Create a route handler for proxy server.
```ts title="/api/proxy/route.ts"
import { openapi } from '@/lib/source';
export const { GET, HEAD, PUT, POST, PATCH, DELETE } = openapi.createProxy();
```
> Follow the [Getting Started](/docs/ui/openapi) guide if `openapi` server is not yet configured.
And enable the proxy from `createOpenAPI`.
```ts title="lib/source.ts"
import { createOpenAPI } from 'fumadocs-openapi/server';
export const openapi = createOpenAPI({
proxyUrl: '/api/proxy',
});
```