Rendering
Render Markdown fields and blocks with server-first components and scoped configuration.
Rendering
Markdown rendering is server-friendly and centralized in the plugin renderer. The same pipeline handles Markdown, GFM, Shiki code blocks, heading anchors, sanitized HTML, and registry-backed directives.
Render A Markdown Field
1import { MarkdownRenderer } from 'payload-markdown/server'2 3export function PostBody({ content }: { content?: string | null }) {4 return (5 <MarkdownRenderer6 collectionSlug="posts"7 markdown={content}8 scope="field"9 />10 )11}
Set collectionSlug when you want collection-scoped code, themes, or config to apply.
Renderer Props
Common props:
markdown: Markdown source stringcollectionSlug: collection key used for config resolutionscope:fieldorblocksvariant:blog,docs,compact, orunstyledsize:lg,md, orsmas: HTML tag for the rendered Markdown container, defaulting toarticleclassName: class names for the Markdown elementwrapperClassName: class names for the outer wrapperenableGutter: adds horizontal wrapper paddingmutedHeadings: slightly reduces heading contrastcolorScheme:auto(follow the site's dark mode),light, ordark; see Light And DarkemptyFallback: returned for empty or whitespace-only MarkdownerrorFallback: returned when compilation fails (the result haserrors); warnings alone do not trigger itcode: local Shiki and code fence overridesicons: local SVG icon pack overridesthemes: local directive theme overrides
Local Overrides
Direct component props are the final override layer:
1<MarkdownRenderer2 as="section"3 code={{4 fullBleed: true,5 lineNumbers: true,6 }}7 collectionSlug="posts"8 emptyFallback={<p>No content yet.</p>}9 markdown={post.content}10 scope="field"11 size="lg"12 themes={{13 card: {14 items: [15 {16 name: 'localFeature',17 classes: 'rounded-2xl border border-cyan-400/40 bg-cyan-950/30 p-5',18 },19 ],20 },21 }}22 variant="blog"23 wrapperClassName="max-w-4xl"24/>
Most applications should prefer plugin-level or collection-level config. Renderer-level overrides are useful for previews, one-off marketing pages, and admin-only rendering surfaces.
Render Blocks
1import { MarkdownBlockComponent } from 'payload-markdown/server'2 3export function MarkdownLayoutBlock({4 block,5 collectionSlug,6}: {7 block: {8 blockType: 'vlMdBlock'9 content: string10 }11 collectionSlug?: string12}) {13 return <MarkdownBlockComponent {...block} collectionSlug={collectionSlug} />14}
MarkdownBlockComponent resolves block scoped defaults with resolveMarkdownBlockDefaults(collectionSlug) and passes scope="blocks" to MarkdownRenderer.
Sanitized HTML And GFM
The render pipeline uses remark-gfm, rehype-raw, and rehype-sanitize. Markdown tables, task lists, strikethrough, and raw HTML are parsed, but unsupported or unsafe HTML is sanitized. Authored inline style attributes are stripped before final output. Raw HTML cannot use the renderer's internal data-* markers (such as data-vl-layout) to impersonate directives, and id/name values in raw HTML receive the standard user-content- prefix to prevent DOM clobbering. Heading, footnote, and tab ids generated by the renderer are not prefixed.
When warnings matter
Rendering reports warnings for malformed directives, invalid directive attributes, unknown themes, and renderer failures. Fatal failures are also listed in errors and logged on the server. MarkdownRenderer returns errorFallback only when compilation fails and errorFallback is provided; non-fatal warnings still render the HTML.
Headless Rendering
payload-markdown/render renders Markdown without React, Next.js or CSS imports, so it works in plain Node scripts, search indexers, feeds, emails and docs tooling:
1import { renderMarkdown } from 'payload-markdown/render'2 3const result = await renderMarkdown(markdown)4 5result.html // sanitized HTML, identical to MarkdownRenderer's inner HTML6result.headings // [{ depth, id, text }]: the heading ids emitted in html7result.text // plain text without directive markup or raw HTML8result.links // [{ url, kind: 'link' | 'image' | 'definition' | 'directive' }]9result.diagnostics // [{ message, severity, source, line?, column?, code? }]10result.warnings // diagnostic messages, as before11result.errors // fatal errors; html is a placeholder when non-empty
renderMarkdown(markdown, config) applies the same plugin defaults as MarkdownRenderer: pass scope, collectionSlug and, when a process builds several Payload configs, settings (a Payload config or instance). See Where Settings Live.
diagnostics carry a source (directive, theme, icon, code or render), a severity (error, warning or info) and, when the pipeline knows it, the 1-based line and column in the Markdown source. The same entry also exports compileMarkdown ({ html, warnings, errors }) and the heading anchor helpers extractHeadingAnchors, slugifyHeading and createHeadingSlugger.
Stylesheet
MarkdownRenderer imports its stylesheet for Next.js apps. Other consumers of the HTML, such as headless renders, can import the same file explicitly:
1import 'payload-markdown/styles.css'