---
title: Algolia Search
description: Integrate Algolia Search with Fumadocs
---

<Callout title="Notice">
  If you're using Algolia's free tier, you have to [display their logo on your search
  dialog](https://algolia.com/policies/free-services-terms).
</Callout>

## Setup

Install dependencies:

```package-install
algoliasearch
```

### Sign up on Algolia

Sign up and obtain the app id and API keys for your search. Store these
credentials in environment variables.

### Sync Search Indexes

Pre-render a static route `/static.json` to export search indexes into production build:

```ts title="lib/export-search-indexes.ts"
import { source } from '@/lib/source';
import type { DocumentRecord } from 'fumadocs-core/search/algolia';

export async function exportSearchIndexes() {
  const results: DocumentRecord[] = [];

  for (const page of source.getPages()) {
    results.push({
      _id: page.url,
      structured: page.data.structuredData,
      url: page.url,
      title: page.data.title,
      description: page.data.description,
    });
  }

  return results;
}
```

<include>.static-route-example.mdx</include>

Make a script to sync search indexes:

```ts title="scripts/sync-content.ts" twoslash
import { algoliasearch } from 'algoliasearch';
import { sync, DocumentRecord } from 'fumadocs-core/search/algolia';
import * as fs from 'node:fs';

const filePath = '<see below>';
const content = fs.readFileSync(filePath);

const records = JSON.parse(content.toString()) as DocumentRecord[];

const client = algoliasearch('id', 'key');

// update the index settings and sync search indexes
void sync(client, {
  indexName: 'document',
  documents: records,
});
```

<include>.static-route-output.mdx</include>

Now run the script after build:

```json title="package.json"
{
  "scripts": {
    "build": "... && bun ./scripts/sync-content.ts"
  }
}
```

### Workflow

You may manually upload search indexes with the script, or integrate it with your CI/CD pipeline.

### Search UI

You can consider different options for implementing the UI:

- Using [Fumadocs UI search dialog](/docs/search/algolia).
- Build your own using the built-in search client hook:

  ```ts twoslash
  import { liteClient } from 'algoliasearch/lite';
  import { useDocsSearch } from 'fumadocs-core/search/client';
  import { algoliaClient } from 'fumadocs-core/search/client/algolia';

  const algolia = liteClient('id', 'key');

  const { search, setSearch, query } = useDocsSearch({
    client: algoliaClient({
      indexName: 'document',
      client: algolia,
    }),
  });
  ```

- Use their official clients directly.

## Advanced

### Tag Filter

To configure tag filtering, add a `tag` value to indexes.

```ts title="lib/export-search-indexes.ts"
import { source } from '@/lib/source';
import type { DocumentRecord } from 'fumadocs-core/search/algolia';

export async function exportSearchIndexes() {
  const results: DocumentRecord[] = [];

  for (const page of source.getPages()) {
    results.push({
      _id: page.url,
      structured: page.data.structuredData,
      url: page.url,
      title: page.data.title,
      description: page.data.description,
      // [!code ++]
      tag: '<your value>',
    });
  }

  return results;
}
```

And update your search client:

- **Fumadocs UI**: Enable [Tag Filter](/docs/search/algolia#tag-filter) on Search UI.
- **Search Client**: You can add the tag filter like:

  ```ts
  import { useDocsSearch } from 'fumadocs-core/search/client';
  import { algoliaClient } from 'fumadocs-core/search/client/algolia';

  const { search, setSearch, query } = useDocsSearch({
    client: algoliaClient({
      tag: '<your tag value>',
      // ...
    }),
  });
  ```

The `tag` field is an attribute for faceting. You can also use the filter `tag:value` on Algolia search clients.

### Under the Hood

The Algolia Integration automatically configures Algolia Search for document search.

It creates a record for **each paragraph** in your document, it is also recommended by Algolia.

Each record contains searchable attributes:

| Attribute | Description           |
| --------- | --------------------- |
| `title`   | Page Title            |
| `section` | Heading ID (nullable) |
| `content` | Paragraph content     |

The `section` field only exists in paragraphs under a heading. Headings and
paragraphs are indexed as an individual record, grouped by their page ID.

Notice that it expects the `url` property of a page to be unique, you shouldn't have two pages with the same
url.
