146 lines
4.9 KiB
Markdown
146 lines
4.9 KiB
Markdown
---
|
|
title: "SEO Plugin"
|
|
aliases: [payload-seo, plugin-seo, meta-fields]
|
|
tags: [payloadcms, plugins, seo, meta, admin]
|
|
sources: [raw/plugins__seo.md]
|
|
created: 2026-05-15
|
|
updated: 2026-05-15
|
|
---
|
|
|
|
`@payloadcms/plugin-seo` adds a managed `meta` field group (title, description, image) to collections and globals, with auto-generate functions and a real-time search-engine preview.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
pnpm add @payloadcms/plugin-seo
|
|
```
|
|
|
|
## Basic Setup
|
|
|
|
```ts
|
|
import { seoPlugin } from '@payloadcms/plugin-seo'
|
|
|
|
buildConfig({
|
|
plugins: [
|
|
seoPlugin({
|
|
collections: ['pages'],
|
|
uploadsCollection: 'media',
|
|
generateTitle: ({ doc }) => `Website.com — ${doc.title}`,
|
|
generateDescription: ({ doc }) => doc.excerpt,
|
|
}),
|
|
],
|
|
})
|
|
```
|
|
|
|
## Options Reference
|
|
|
|
| Option | Type | Description |
|
|
|--------|------|-------------|
|
|
| `collections` | `string[]` | Collection slugs to enable SEO on |
|
|
| `globals` | `string[]` | Global slugs to enable SEO on |
|
|
| `uploadsCollection` | `string` | Upload collection slug for the `image` subfield |
|
|
| `tabbedUI` | `boolean` | Append an SEO tab (default: `false`) |
|
|
| `fields` | `fn` | Extend or override the default meta fields |
|
|
| `generateTitle` | `fn` | Return a custom meta title from doc data |
|
|
| `generateDescription` | `fn` | Return a custom meta description from doc data |
|
|
| `generateImage` | `fn` | Return a custom meta image from doc data |
|
|
| `generateURL` | `fn` | Return the canonical URL shown in the preview |
|
|
| `interfaceName` | `string` | Rename the generated TypeScript/GraphQL interface |
|
|
|
|
### `fields` — adding custom fields
|
|
|
|
```ts
|
|
seoPlugin({
|
|
fields: ({ defaultFields }) => [
|
|
...defaultFields,
|
|
{ name: 'ogTitle', type: 'text' },
|
|
],
|
|
})
|
|
```
|
|
|
|
### Generate function arguments
|
|
|
|
All `generate*` functions receive the same argument object:
|
|
|
|
| Arg | Description |
|
|
|-----|-------------|
|
|
| `doc` | Current document data |
|
|
| `req` | Payload request (`user`, `payload`, `i18n`) |
|
|
| `locale` | Active locale |
|
|
| `collectionSlug` / `globalSlug` | Slug of the owning collection/global |
|
|
| `id` | Document ID |
|
|
| `publishedDoc` | The published version |
|
|
| `hasPublishPermission` / `hasSavePermission` | User permission flags |
|
|
|
|
## `tabbedUI` Note
|
|
|
|
When `tabbedUI: true`, the plugin appends an **SEO** tab. If your collection has no existing `tabs` field as its first field, Payload creates a **Content** tab automatically.
|
|
|
|
> If you need sidebar/top-level fields alongside `tabbedUI`, define the first field as `type: 'tabs'` yourself — don't let Payload auto-create the Content tab.
|
|
|
|
## Direct Field Imports
|
|
|
|
Import fields individually for fine-grained placement:
|
|
|
|
```ts
|
|
import {
|
|
MetaTitleField,
|
|
MetaDescriptionField,
|
|
MetaImageField,
|
|
PreviewField,
|
|
OverviewField,
|
|
} from '@payloadcms/plugin-seo/fields'
|
|
|
|
MetaImageField({ relationTo: 'media', hasGenerateFn: true })
|
|
MetaDescriptionField({ hasGenerateFn: true })
|
|
MetaTitleField({ hasGenerateFn: true })
|
|
|
|
PreviewField({
|
|
hasGenerateFn: true,
|
|
titlePath: 'meta.title',
|
|
descriptionPath: 'meta.description',
|
|
})
|
|
|
|
OverviewField({
|
|
titlePath: 'meta.title',
|
|
descriptionPath: 'meta.description',
|
|
imagePath: 'meta.image',
|
|
})
|
|
```
|
|
|
|
> You still need the plugin registered in `buildConfig` to configure the generation functions. Direct imports don't inherit plugin config automatically.
|
|
|
|
Override character limits via `minLength`/`maxLength` on individual field components, or `titleOverrides`/`descriptionOverrides` on `OverviewField`.
|
|
|
|
## TypeScript
|
|
|
|
```ts
|
|
import type { GenerateTitle, GenerateDescription, GenerateURL } from '@payloadcms/plugin-seo/types'
|
|
import type { Page } from './payload-types'
|
|
|
|
const generateTitle: GenerateTitle<Page> = async ({ doc }) =>
|
|
`Website.com — ${doc?.title}`
|
|
```
|
|
|
|
## Key Takeaways
|
|
|
|
- Plugin adds `meta.title`, `meta.description`, `meta.image` fields to enabled collections/globals
|
|
- "Auto-generate" buttons call your custom `generate*` functions — great for AI-assisted or rule-based SEO
|
|
- Real-time search-engine preview and character counters help editors write effective meta without leaving the Admin Panel
|
|
- Extend meta fields with `og:title`, `json-ld`, or any custom field via the `fields` option
|
|
- `tabbedUI: true` wraps fields in a tab — requires the first field to already be a `tabs` field to avoid an unwanted Content tab auto-creation
|
|
- Direct field imports give placement flexibility but still require the plugin to be registered for `generate*` functions to work
|
|
- All `generate*` functions share the same argument shape — use `req.payload` for DB lookups or third-party AI calls
|
|
|
|
## Related
|
|
|
|
- [[wiki/payloadcms/plugins|Plugins Overview + Official]]
|
|
- [[wiki/payloadcms/plugin-search|Search Plugin]]
|
|
- [[wiki/payloadcms/plugin-nested-docs|Nested Docs Plugin]]
|
|
- [[wiki/payloadcms/collection-config|Collection Config]]
|
|
- [[wiki/payloadcms/fields-tabs|Tabs Field]]
|
|
- [[wiki/payloadcms/custom-components|Custom Components]]
|
|
|
|
## Sources
|
|
|
|
- `raw/plugins__seo.md` — https://payloadcms.com/docs/plugins/seo
|