---
title: Fumadocs v14
description: Introducing Fumadocs v14
author: Fuma Nama
date: 2024-09-19
---

## Why?

Taking some inspirations from [Shadcn UI](https://ui.shadcn.com), I decided to make Fumadocs easier to use, with more APIs that simplify the design.

This is a large update.

## New Features

### Fumadocs CLI

Clone Fumadocs UI components to your workspace and modify them.

```package-install
npm install @fumadocs/cli --save-dev
```

```bash
pnpm fumadocs add
```

See [docs](/docs/cli) for details.

### Sidebar Tabs

Previously, multiple page tree is implemented with Fumadocs UI `RootToggle` component.
You have to add it to the sidebar banner which isn't as intuitive as other APIs.

With Sidebar Tabs, by creating a root folder, Fumadocs will immediately add a tabs component to the sidebar.

```json title="meta.json"
{
  "root": true,
  "title": "Tab Name",
  "description": "Some text about the tab",
  "icon": "IconName"
}
```

### Orama

We migrated the built-in search from Flexsearch to Orama, the same search engine that powers the Node.js docs site.
It is open source, and also have their Cloud integration.

No changes required.
You can use our new `createFromSource` API to create the route handler, which offers i18n support without any configurations.

```ts
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';

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

In favour of this, the new search client of Fumadocs is introduced with support for different API clients.
This includes Algolia, Orama (static and dynamic with route handlers).

```ts
import { useDocsSearch } from 'fumadocs-core/search/client';
import { oramaStaticClient } from 'fumadocs-core/search/client/orama-static';

const client = useDocsSearch({
  client: oramaStaticClient(),
});
```

See [Search API](/docs/headless/search).

### Static Search

For sites with static export, you cannot use route handlers.
We now support client-side search using search indexes generated during build time.

See [Static Search](/docs/headless/search/orama#static-export).

### Less Dependencies

I'm always working to reduce the dependencies required for Fumadocs, that is one reason why we separated Fumadocs into different packages.

Fumadocs UI now bundles icons from `lucide-react`, for Next.js apps that is not using Lucide, they can avoid downloading the entire icon library.

And `swr` is no longer used for search client, we implemented a light `useQuery` hook with extra care on React performance optimization.

### New Metadata Image API

To improve code organization, we made a Metadata Image API on Fumadocs Core.
It is a tiny wrapper over Next.js Metadata API, composes seamlessly with Source API.

```ts title="lib/metadata.ts"
import { createMetadataImage } from 'fumadocs-core/server';
import { source } from '@/lib/source';

export const metadataImage = createMetadataImage({
  imageRoute: '/docs-og',
  source,
});
```

```ts title="route.ts"
import { generateOGImage } from 'fumadocs-ui/og';
import { metadataImage } from '@/lib/metadata';

export const GET = metadataImage.createAPI((page) => {
  return generateOGImage({
    title: page.data.title,
    description: page.data.description,
    site: 'My App',
  });
});

export function generateStaticParams() {
  return metadataImage.generateParams();
}
```

```tsx title="page.tsx"
import { source } from '@/lib/source';
import { metadataImage } from '@/lib/metadata';
import { notFound } from 'next/navigation';

export function generateMetadata({ params }: { params: { slug?: string[] } }) {
  const page = source.getPage(params.slug);
  if (!page) notFound();

  return metadataImage.withImage(page.slugs, {
    title: page.data.title,
    description: page.data.description,
  });
}
```

In favour of this, the `getImageMeta` function from `fumadocs-ui/og` has been removed.

### Shorthands

As you may notice, we introduced more abstractions to reduce the setup steps required for enabling some features.
It is also required for the code generator from Fumadocs CLI to work.

Like the `generateParams` function, it enables zero-configuration i18n support for Next.js `generateStaticParams`.

```ts
import { source } from '@/lib/source';

export function generateStaticParams() {
  return source.generateParams();
}
```

### Better Card Component

Fumadocs UI Card can now support usage without `href`.
You can pass children as React node, Typography styles will be applied correctly.

```mdx
<Card icon={<Database />} title='Content Source'>

The input/source of your content, it can be a CMS, or local data layers like [Content Collections](https://www.content-collections.dev) and [Fumadocs MDX](/docs/mdx).

</Card>
```

### Better Category Component

The original `DocsCategory` component was copied from our official docs, which is a very simple implementation that doesn't take page tree into account.

Now it accepts the source object via `from` prop.

```tsx title="page.tsx"
import { source } from '@/lib/source';
import { DocsCategory } from 'fumadocs-ui/page';

const page = source.getPage(params.slug);

<DocsCategory page={page} from={source} />;
```

By default, it takes the `locale` property from `page` to find the corresponding page tree to traverse.
You can also force a page tree.

```tsx title="page.tsx (i18n enabled)"
import { source } from '@/lib/source';
import { DocsCategory } from 'fumadocs-ui/page';

const page = source.getPage(params.slug, params.lang);

<DocsCategory page={page} from={source} tree={source.pageTree['en']} />;
```

### OpenAPI Tag Display Name

Change the display name with `x-displayName`.

```yaml title="openapi.yaml"
tags:
  - name: test
    description: this is a tag.
    x-displayName: My Test Name
```

### Better TypeScript Docs Automation

The `AutoTypeTable` component now supports a `type` prop, you can use TypeScript inside the field:

```mdx
<AutoTypeTable
  path="file.ts"
  name="B"
  type={`
import { ReactNode } from "react"
export type B = ReactNode | { world: string }
`}
/>
```

And code highlighting in typedoc is also supported with Shiki.

We **highly recommend** to use `createTypeTable` instead of importing the component in MDX files, this allows a single instance of TypeScript Compiler API to be shared.
See [Auto Type Table](/docs/ui/components/auto-type-table).

### Next.js 15

Fumadocs v14 is compatible with Next.js 15, supporting sync and async usages of dynamic APIs.

<Callout type="info" title="Backward compatible">
  Next.js 14 is also supported, notice that guides/tutorials in the docs are mainly for Next.js 15.
</Callout>

## UI Improvements

### Navbar Links

The navigation menu on Home layout is redesigned, with better animation and flexibility.

See the [new API](/docs/navigation).

![New Navbar](/blog/v14-navbar.png)

### Link Styles

You can escape Tailwind CSS Typography styles for the `a` tag with `data-card` attribute.

```jsx
<a data-card="">
  No styles applied <code>But it does</code>
</a>
```

### Disable Theme Switch

You can disable the default theme switcher with `disableThemeSwitch` on layouts.

## Breaking Changes

### Move `dir` option from `defineDocs`

```ts
import { defineDocs } from 'fumadocs-mdx/config';

export const { docs, meta } = defineDocs({
  dir: 'my/content/dir', // define once
});
```

### Refactor Imports

**Previous**

```ts
import { DocsLayout } from 'fumadocs-ui/layout';
import { HomeLayout } from 'fumadocs-ui/home-layout';
import { HomeLayoutProps } from 'fumadocs-ui/home-layout';
```

**New Import Path**

```ts
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
```

### Twoslash UI

We moved Twoslash UI components to `fumadocs-twoslash`.
This isolates some logic from Fumadocs UI, allowing a better code organization.

Import the CSS styles and `Popup` component differently.

```ts
import 'fumadocs-twoslash/twoslash.css';

import { Popup } from 'fumadocs-twoslash/ui';
```

<Callout>It requires Tailwind CSS.</Callout>

### Remove Deprecated APIs

The previous `createI18nSearchAPIExperimental` is now renamed to `createI18nSearchAPI`.
It takes the i18n config instead of scattering the options everywhere.

The types from `fumadocs-core/search/shared` is moved to `fumadocs-core/server`.
