---
title: "Search Plugin"
aliases: [plugin-search, payload-search, search-index]
tags: [payloadcms, plugin, search, indexing]
sources: [raw/plugins__search.md]
created: 2026-05-15
updated: 2026-05-15
---

## Overview

`@payloadcms/plugin-search` creates a dedicated `search` collection that mirrors only search-critical fields from your enabled collections. Records are automatically synced on document create/update/delete via hooks, and can be queried with standard Payload APIs — no third-party service required.

## How It Works

1. Plugin adds a `search` collection to your database with an index
2. Enabled collections get `beforeChange` + `afterDelete` hooks
3. On save → a lightweight search record (title, excerpt, slug, etc.) is created/updated
4. On delete → search record is removed
5. Queries hit the `search` collection directly — bypassing all hooks on the original collection

## Installation

```bash
pnpm add @payloadcms/plugin-search
```

## Basic Setup

```ts
import { searchPlugin } from '@payloadcms/plugin-search'

export default buildConfig({
  plugins: [
    searchPlugin({
      collections: ['pages', 'posts'],
      defaultPriorities: {
        pages: 10,
        posts: 20,
      },
    }),
  ],
})
```

## Options Reference

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `collections` | `string[]` | required | Slugs of collections to index |
| `localize` | `boolean` | `true` | Add localization to `title` field if i18n is enabled |
| `defaultPriorities` | `Record<slug, number \| fn>` | — | Fallback `priority` on create; fn receives `doc` |
| `searchOverrides` | `Partial<CollectionConfig>` | — | Merge into the auto-created `search` collection config |
| `beforeSync` | `afterChange hook` | — | Modify/fallback search doc data before create/update |
| `syncDrafts` | `boolean` | `false` | Sync draft documents to search index |
| `deleteDrafts` | `boolean` | `true` | Delete search record when doc status changes to draft |
| `skipSync` | `async fn` | — | Return `true` to skip a specific doc/locale combination |
| `reindexBatchSize` | `number` | `50` | Batch size for on-demand reindexing |

### `beforeSync` — Modify Data Before Index

```ts
searchPlugin({
  beforeSync: ({ originalDoc, searchDoc }) => ({
    ...searchDoc,
    excerpt: originalDoc?.excerpt || 'Fallback excerpt',
  }),
})
```

### `searchOverrides` — Extend the Search Collection

```ts
searchPlugin({
  searchOverrides: {
    slug: 'search-results',
    fields: ({ defaultFields }) => [
      ...defaultFields,
      { name: 'excerpt', type: 'textarea' },
    ],
  },
})
```

### `skipSync` — Conditional Indexing

```ts
searchPlugin({
  skipSync: async ({ locale, doc, collectionSlug, req }) => {
    if (!locale) return false
    const tenant = await req.payload.findByID({ collection: 'tenants', id: doc.tenant.id })
    return !tenant.allowedLocales.includes(locale)
  },
})
```

`skipSync` is called **once per locale per document**. Return `true` = skip, `false` = proceed.

## Priority & Sorting

- Every search record gets a `priority` field
- Use `?sort=priority` (ascending) to order results by collection type
- `defaultPriorities` sets the initial value at create time
- Can be a static number or a function: `({ doc }) => doc.featured ? 1 : 10`

## On-Demand Reindexing

- Navigate to the `search` collection in the Admin Panel
- Click the **Reindex** pill (top-right action slot)
- Select a specific collection or "all" to rebuild indexes
- Useful when adding the plugin to an existing project with existing documents

## TypeScript

```ts
import type { SearchConfig, BeforeSync } from '@payloadcms/plugin-search/types'
```

## Key Takeaways

- **First-party Algolia alternative** — no external service; runs on your own DB
- **Static snapshots bypass hooks** — queries on `search` collection don't trigger hooks on original docs, making them significantly faster
- **You control what's indexed** — only sync fields you need (title, slug, excerpt)
- **Priority field enables ranked results** — pass `?sort=priority` to order by collection or document importance
- **`skipSync` is per locale** — called once per locale per document; useful for multi-tenant locale filtering
- **Draft handling is opt-in** — `syncDrafts: false` by default; set `deleteDrafts: true` to remove drafts from search
- **Reindex button** covers existing data migration when adding plugin to existing projects

## Related

- [[wiki/payloadcms/plugins|Plugins Overview + Official]] — all 10 official plugins
- [[wiki/payloadcms/plugins-api|Plugin API]] — build custom plugins
- [[wiki/payloadcms/hooks-collections|Collection Hooks]] — `beforeChange`/`afterDelete` hooks used internally
- [[wiki/payloadcms/queries|Queries]] — querying the `search` collection with `?sort=priority`
- [[wiki/payloadcms/localization|Localization]] — `localize` option and `skipSync` locale parameter

## Sources

- `raw/plugins__search.md` — https://payloadcms.com/docs/plugins/search
