Vaulthalla Logo

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 blocks field, the plugin adds a Markdown text field
  • if the collection has a blocks field, the plugin installs MarkdownBlock into 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}
Do not wrap the block in a block prop

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.