Fields And Blocks
Understand automatic field and block installation, manual schema control, and block rendering requirements.
Fields And Blocks
Payload Markdown supports both direct Markdown fields and reusable Payload blocks. Both store Markdown source; the difference is how the content is placed in your schema.
Automatic Install Behavior
1payloadMarkdown({2 collections: {3 pages: true,4 posts: true,5 },6})
For each enabled collection:
- if the collection has no
blocksfield, the plugin adds a Markdown text field - if the collection has a
blocksfield, the plugin installsMarkdownBlockinto that field - if both are disabled explicitly, the collection is recorded in plugin settings but no schema field is added
Explicit Install Options
Use installField and installIntoBlocks when inference is not what you want.
1payloadMarkdown({2 collections: {3 pages: {4 fieldName: 'body',5 installField: true,6 installIntoBlocks: true,7 },8 },9})
This is useful for page collections that have a layout builder but also need a primary Markdown body.
Manual Markdown Field
Use markdownField() when you want direct schema ownership.
1import { markdownField } from '@valkyrianlabs/payload-markdown'2 3export const Posts = {4 slug: 'posts',5 fields: [6 markdownField({7 name: 'body',8 label: 'Body Markdown',9 localized: true,10 required: true,11 }),12 ],13}
markdownField() returns a Payload text field with the admin field component already wired to PayloadMarkdownField.
Manual Block Registration
Use MarkdownBlock when you want to place the block yourself.
1import { MarkdownBlock } from '@valkyrianlabs/payload-markdown'2 3export const Pages = {4 slug: 'pages',5 fields: [6 {7 name: 'layout',8 type: 'blocks',9 blocks: [MarkdownBlock],10 },11 ],12}
The block slug is vlMdBlock. Generated Payload types use that value as the block type discriminator.
Render Block Entries
MarkdownBlockComponent accepts the block data fields directly. In a block renderer, pass the block object through as props and include collectionSlug when collection-scoped config should apply.
1import { MarkdownBlockComponent } from '@valkyrianlabs/payload-markdown/server'2 3const blockComponents = {4 vlMdBlock: MarkdownBlockComponent,5}6 7export function RenderBlocks({8 blocks,9 collectionSlug,10}: {11 blocks?: Array<{ blockType?: string }>12 collectionSlug?: string13}) {14 if (!blocks?.length) return null15 16 return blocks.map((block, index) => {17 const Block = block.blockType ? blockComponents[block.blockType] : undefined18 if (!Block) return null19 20 return <Block {...block} collectionSlug={collectionSlug} key={index} />21 })22}
MarkdownBlockComponent reads content, blockType, id, and blockName from its own props. Pass {...block}, not block={block}.
Field And Block Presentation
Use a split config object when fields and blocks need different typography or wrappers:
1payloadMarkdown({2 collections: {3 pages: {4 config: {5 blocks: {6 size: 'md',7 variant: 'docs',8 },9 field: {10 size: 'lg',11 variant: 'blog',12 },13 },14 },15 },16})
MarkdownBlockComponent resolves block scope internally. Direct field rendering should use scope="field" or omit scope, since field is the default.
