Flexsearch

Built-in document search of Fumadocs

Fumadocs supports searching document based on Flexsearch.

As the bulit-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.

  1. Create the API Route.
app/api/search/route.ts
import { createSearchAPI } from 'fumadocs-core/search/server';

export const { GET } = createSearchAPI('simple', {
  indexes: allDocs.map((docs) => ({
    title: docs.title,
    content: docs.body.raw, // Raw Content
    url: docs.url,
  })),
});
  1. Create a Search Dialog.
import { useDocsSearch } from 'fumadocs-core/search/client';

export function Dialog() {
  const { search, setSearch, query } = useDocsSearch();
  return <div>...</div>;
}

createSearchAPI

Create a GET route handler, that supports simple and advanced search.

useDocsSearch

A hook that combined 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.

Internationalization

The default createSearchAPI doesn't provide functionality for i18n. Instead, you should use createI18nSearchAPI.

  1. Update the route handler.
api/search/route.ts
import { languages } from '@/i18n';
import { getPages } from '@/source';
import { createI18nSearchAPI } from 'fumadocs-core/search/server';

export const { GET } = createI18nSearchAPI('advanced', {
  indexes: languages.map((lang) => {
    const pages = getPages(lang)!.map((page) => ({
      ...
    }));

    return [lang, pages];
  }),
});
  1. Add locale to search dialog, this will only allow pages with specified locale to be searched by the user.
function Dialog() {
  const { search, setSearch, query } = useDocsSearch(locale);

  //...
}

Returns a more detailed result with matching headings and contents.

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

Notice that it cannot extract content from rehype-specific content (remark plugins are supported)

Usage

Same as simple search, but requires the structuredData property.

import { allDocs } from 'contentlayer/generated';
import { createSearchAPI } from 'fumadocs-core/search/server';

export const { GET } = await createSearchAPI('advanced', {
  indexes: allDocs.map((docs) => ({
    id: docs._id,
    title: docs.title,
    url: '/docs/' + docs.slug,
    structuredData: docs.structuredData,
  })),
});

Tag Filter

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

import { allDocs } from 'contentlayer/generated';
import { createSearchAPI } from 'fumadocs-core/search/server';

export const { GET } = createSearchAPI('advanced', {
  indexes: allDocs.map((page) => ({
    ...page,
    tag: 'value',
  })),
  tag: true,
});

// Pass `tag` in your custom search dialog
const { search, setSearch, query } = 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.

Last updated on