Fumadocs

Built-in Search

Built-in document search of Fumadocs

Fumadocs supports document search with ZBSearch, It is the default but also the recommended option since it can be self-hosted and totally free.

Setup

Host the server for handling search requests.

From Source

Create the server from source object.

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

export const { GET } = createFromSource(source);

From Search Indexes

Create the server from search indexes, each index needs a structuredData field.

Usually, it is provided by your content source (e.g. Fumadocs MDX). You can also extract it from Markdown/MDX document using the Remark Structure plugin.

app/api/search/route.ts
import { source } from '@/lib/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,
    url: page.url,
    id: page.url,
    structuredData: page.data.structuredData,
  })),
});

Searching Documents

You can search documents using:

  • Fumadocs UI: Supported out-of-the-box, see Search UI for details.
  • Search Client:
import {  } from 'fumadocs-core/search/client';
import {  } from 'fumadocs-core/search/client/fetch';

const  = ({
  : (),
});

Prop

Type

Configurations

Tag Filter

Support filtering results by tag, it's useful for implementing multi-docs similar to this documentation.

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

const server = createFromSource(source, {
  buildIndex(page) {
    return {
      title: page.data.title,
      description: page.data.description,
      url: page.url,
      id: page.url,
      structuredData: page.data.structuredData,
      // use your desired value, like page.slugs[0]
      tag: '<value>',
    };
  },
});

and update your search client:

  • Fumadocs UI: Configure Tag Filter on Search UI.
  • Search Client: pass a tag to fetchClient.
import { useDocsSearch } from 'fumadocs-core/search/client';
import { fetchClient } from 'fumadocs-core/search/client/fetch';

const client = useDocsSearch({
  client: fetchClient({
    tag: '<value>',
  }),
});

Static Mode

To support usage with static site, use staticGET from search server and make the route static or pre-rendered.

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

// statically cached
export const revalidate = false;
export const { staticGET: GET } = createFromSource(source);

staticGET is also available on createSearchAPI.

and update your search clients:

  • Fumadocs UI: use static client on Search UI.

  • Search Client: use staticClient instead of fetchClient.

    import { useDocsSearch } from 'fumadocs-core/search/client';
    import { staticClient } from 'fumadocs-core/search/client/orama-static';
    
    const client = useDocsSearch({
      client: staticClient(),
    });

    Prop

    Type

Be Careful

Static Search requires clients to download the exported search indexes. For large docs sites, it can be expensive.

You should use cloud solutions like Orama Cloud or Algolia for these cases.

Internationalization

Search works with every language out of the box - the default multilingual mode uses Unicode word segmentation, so all locales (including Chinese and Japanese) share a single search database with zero config.

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

// no extra configuration needed for i18n
const server = createFromSource(source);

and update your search clients:

  • Fumadocs UI: No changes needed, Fumadocs UI handles this when you have i18n configured correctly.
  • Search Client: Add locale to the search client, this will only allow pages with specified locale to be searchable by the user.
import { useDocsSearch } from 'fumadocs-core/search/client';
import { fetchClient } from 'fumadocs-core/search/client/fetch';

const { search, setSearch, query } = useDocsSearch({
  client: fetchClient({
    locale: 'cn',
  }),
});

This also applies to Static Mode, pass locale to staticClient instead.

Language-specific Tokenization

The multilingual mode doesn't apply language-specific stemming or stop-words. To enable them for a language on the supported languages list, you can use localeMap:

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

const server = createFromSource(source, {
  localeMap: {
    // [locale]: search options
    ru: { language: 'russian' },
    en: { language: 'english' },
  },
});

Note that a separate search database is created for each locale when it's enabled.

Headless

You can host the search server on other backend such as Express and Elysia.

import { initAdvancedSearch } from 'fumadocs-core/search/server';

const server = initAdvancedSearch({
  // you still have to pass indexes
});

server.search('query', {
  // you can specify `locale` and `tag` here
});

How is this guide?

Last updated on

On this page