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

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).


Same as simple search, but requires the structuredData property.

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

Tag Filter

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

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

For Fumadocs UI, configure Tag Filter.

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.

"page" | "text" | "heading"


Use createI18nSearchAPI for i18n functionality.

Update the route handler.

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];

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

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

Response Data

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

Last updated on

On this page

Edit on Github