SEO tab
@bison-lab/payload-core wraps @payloadcms/plugin-seo so every Bison Lab site gets the same tab without copying the configuration. Every string a site would spell is an option. The plugin never reads process.env and never assumes a route.
What editors get
Section titled “What editors get”An SEO tab beside the page’s Content tab, never below the block editor: an overview with character counts, meta title and description with Generate buttons, a share image from the media collection with its own Generate button when the site can name an image already on the page, a search-result preview, and a Hide this page from search engines switch, off by default.
Wiring
Section titled “Wiring”-
Configure the plugin. The site spells its name and its URL scheme once.
payload.config.ts import { firstImageIn, seoPlugin } from "@bison-lab/payload-core";import type { Page } from "@/payload-types";export default buildConfig({plugins: [seoPlugin<Page>({siteName: "Acme Clinics",urlFor: (doc) => (doc.slug ? absoluteUrl(pagePath(doc.slug)) : undefined),describeFrom: (doc) => doc.hero?.[0]?.body,imageFor: (doc) => firstImageIn([...(doc.hero ?? []), ...(doc.layout ?? [])]),}),],});collectionsdefaults to["pages"]anduploadsCollectionto"media".urlForansweringundefinedleaves the preview empty rather than pointing it at the home page while the slug is still blank.describeFromandimageForare optional; without them those Generate buttons fill in nothing, and the image button does not appear at all. -
Cut the share rendition. Add
SHARE_IMAGE_SIZEto the upload collection so every image gets a 1200 by 630 card:import { SHARE_IMAGE_SIZE } from "@bison-lab/payload-core";export const Media: CollectionConfig = {slug: "media",upload: { imageSizes: [SHARE_IMAGE_SIZE] },fields: [{ name: "alt", type: "text", required: true }],}; -
Run the three commands.
payload generate:types,payload generate:importmap(the plugin’s admin components resolve by string key from the site’s import map) andpayload migrate:create.
Then read the tab back with pageMetadata.
Changing a field
Section titled “Changing a field”noIndexField and the plugin’s own fields are columns in every consuming site. A change to them is a schema change: the changeset says “run payload migrate:create”.