| name | payload-block-extractor |
| description | Use when a developer adds a new page block to this CMS and needs to wire up semantic search indexing for it. Triggers on: "add block to search", "index new block", "wire up extractor", or any mention of a new block needing search support.
|
payload-block-extractor
Use this skill when a new block has been created at apps/cms/src/blocks/<BlockName>/config.ts
and needs its text extraction wired into semantic search.
Each block owns its extractor at apps/cms/src/blocks/<BlockName>/extractText.ts.
The orchestrator that calls all extractors is apps/cms/src/collections/Page/extractPageText.ts.
The two shared utilities are extractLexicalText and joinText from @/lib/utils/text.
Step 1 — Read the block config
Read apps/cms/src/blocks/<BlockName>/config.ts. Identify all fields and their types.
Step 2 — Map fields to extractor expressions
| Field type | Expression |
|---|
text / textarea | block.fieldName |
richText | extractLexicalText(block.fieldName) |
array with sub-fields | (block.items ?? []).flatMap(item => [<recurse sub-fields>]) |
relationship / upload / other | // TODO: manual extraction needed for block.fieldName |
For nested array fields, recurse into sub-fields using the same mapping rules.
Step 3 — Create apps/cms/src/blocks/<BlockName>/extractText.ts
Write the extractor function in the block's own folder:
import type { <BlockName>Block } from '@/payload-types'
import { extractLexicalText, joinText } from '@/lib/utils/text'
export function extract<BlockName>Text(block: <BlockName>Block): string {
return joinText([
])
}
Example — for a block named Promo with a text field, a richText body, and an items array each having a label text field:
import type { PromoBlock } from "@/payload-types";
import { extractLexicalText, joinText } from "@/lib/utils/text";
export function extractPromoText(block: PromoBlock): string {
return joinText([block.text, extractLexicalText(block.body), ...(block.items ?? []).flatMap((item) => [item.label])]);
}
Step 4 — Register the extractor in extractPageText.ts
Open apps/cms/src/collections/Page/extractPageText.ts.
- Add an import for the new extractor function (keep imports sorted alphabetically by block name):
import { extract<BlockName>Text } from '@/blocks/<BlockName>/extractText'
- Add a
case to the switch inside extractPageBlockText:
case 'blockSlug':
return extract<BlockName>Text(block)
The blockSlug is the slug property defined in the block's config.ts.
Step 5 — Lint and type-check
cd apps/cms && bun run check-types && bun run lint
Fix any errors before committing.
Step 6 — Commit
git add apps/cms/src/blocks/<BlockName>/extractText.ts
git add apps/cms/src/collections/Page/extractPageText.ts
git commit -m "feat(search): add text extractor for <BlockName> block"
Relationship fields
When a field is a relationship or upload, emit a // TODO rather than guessing:
Leave the TODO in place and note it in the PR description so a human can decide whether to populate it (e.g. by looking up the related document's title).