Fumadocs

Flexsearch

Built-in document search of Fumadocs

Fumadocs supports searching document based on Flexsearch.

As the built-in search of Fumadocs, It is the default but also recommended option since it's easier to setup and totally free.

If you're using a CMS, you should use the API provided by the CMS instead.

Index with the raw content of document.

Create the Route Handler

app/api/search/route.ts
import { allDocs } from 'content-collections';
import { createSearchAPI } from 'fumadocs-core/search/server';
 
export const { GET } = createSearchAPI('simple', {
  indexes: allDocs.map((docs) => ({
    title: docs.title,
    content: docs.content // Raw Content
    url: docs.url,
  })),
});

Create a Search Dialog

For Fumadocs UI

Use the default Search Dialog from Fumadocs UI.

import { useDocsSearch } from 'fumadocs-core/search/client';
 
export function Dialog() {
  const { search, setSearch, query } = useDocsSearch();
 
  return <div>...</div>;
}

Returns a more detailed result with matching headings and contents.

It accepts structured data processed from a markdown/MDX document, and index it with Flexsearch. You can extract the structured data using the Structure remark plugin.

It cannot extract content from rehype plugin generated content (remark plugins are supported).

Usage

Same as simple search, but requires the structuredData property instead of raw content.

app/api/search/route.ts
import { source } from '@/app/source';
import { createSearchAPI } from 'fumadocs-core/search/server';
 
export const { GET } = createSearchAPI('advanced', {
  indexes: source.getPages().map((page) => ({
    title: page.data.title,
    description: page.data.description,
    structuredData: page.data.structuredData,
    id: page.url,
    url: page.url,
  })),
});

Tag Filter

It's useful for implementing versioned docs, or multi-docs similar to this documentation.

import { source } from '@/app/source';
import { createSearchAPI } from 'fumadocs-core/search/server';
 
export const { GET } = createSearchAPI('advanced', {
  indexes: source.getPages().map((page) => ({
    tag: 'your value here',
    // other props
  })),
  tag: true,
});

For Fumadocs UI

Configure Tag Filter on search client.

On your search client, pass a tag to the hook.

import { useDocsSearch } from 'fumadocs-core/search/client';
 
// Pass `tag` in your custom search dialog
const client = useDocsSearch('locale', 'tag');

Custom Algorithm

You can port your own search algorithm by returning a list of SortedResult from your custom /api/search/route.ts api endpoint, and you can integrate it with Fumadocs UI later.

PropTypeDefault
id
string
-
url
string
-
type
"page" | "text" | "heading"
-
content
string
-

Internationalization

Use createI18nSearchAPI for i18n functionality.

Update the route handler

app/api/search/route.ts
import { source } from '@/app/source';
import { i18n } from '@/i18n';
import { createI18nSearchAPIExperimental } from 'fumadocs-core/search/server';
 
export const { GET } = createI18nSearchAPIExperimental('advanced', {
  i18n,
  indexes: source.getLanguages().flatMap((entry) => {
    return entry.pages.map((page) => ({
      id: page.url,
      url: page.url,
      title: page.data.title,
      structuredData: page.data.structuredData,
      locale: entry.language,
    }));
  }),
});

Update search client

Add locale to search dialog, this will only allow pages with specified

locale to be searched by the user.

For Fumadocs UI

You can ignore this, Fumadocs UI handles this when you have i18n configured correctly.

function Dialog() {
  const { search, setSearch, query } = useDocsSearch(locale);
 
  //...
}

Search Client

useDocsSearch is a hook that combines debounce value and useSWR.

Return Type

PropTypeDescription
querySWRResponseSWR Query
searchstringSearching text (not debounced)
setSearch(v: string) => voidSet searching text

Response Data

Type
emptyIf the searching text is empty or blank
SortedResult[]Array of matching pages, headings and contents.

Headless

You can also host the search server on Express or Elysia, without Next.js.

import { initAdvancedSearch } from 'fumadocs-core/search/server';
 
const server = initAdvancedSearch({
  // options
});
 
server.search('query', {
  // you can specify `locale` and `tag` here
});

Last updated on

On this page

Edit on GitHub