Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Converting Markdown

Converting Richtext to Markdown

If you have access to the Payload Config and the lexical editor config, you can convert the lexical editor state to Markdown with the following:

1
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
2
3
import {
4
convertLexicalToMarkdown,
5
editorConfigFactory,
6
} from '@payloadcms/richtext-lexical'
7
8
// Your richtext data here
9
const data: SerializedEditorState = {}
10
11
const html = convertLexicalToMarkdown({
12
data,
13
editorConfig: await editorConfigFactory.default({
14
config, // <= make sure you have access to your Payload Config
15
}),
16
})

Example - outputting Markdown from the Collection

1
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
2
import type { CollectionConfig, RichTextField } from 'payload'
3
4
import {
5
convertLexicalToMarkdown,
6
editorConfigFactory,
7
lexicalEditor,
8
} from '@payloadcms/richtext-lexical'
9
10
const Pages: CollectionConfig = {
11
slug: 'pages',
12
fields: [
13
{
14
name: 'nameOfYourRichTextField',
15
type: 'richText',
16
editor: lexicalEditor(),
17
},
18
{
19
name: 'markdown',
20
type: 'textarea',
21
admin: {
22
hidden: true,
23
},
24
hooks: {
25
afterRead: [
26
({ siblingData, siblingFields }) => {
27
const data: SerializedEditorState =
28
siblingData['nameOfYourRichTextField']
29
30
if (!data) {
31
return ''
32
}
33
34
const markdown = convertLexicalToMarkdown({
35
data,
36
editorConfig: editorConfigFactory.fromField({
37
field: siblingFields.find(
38
(field) =>
39
'name' in field && field.name === 'nameOfYourRichTextField',
40
) as RichTextField,
41
}),
42
})
43
44
return markdown
45
},
46
],
47
beforeChange: [
48
({ siblingData }) => {
49
// Ensure that the markdown field is not saved in the database
50
delete siblingData['markdown']
51
return null
52
},
53
],
54
},
55
},
56
],
57
}

Converting Markdown to Richtext

If you have access to the Payload Config and the lexical editor config, you can convert Markdown to the lexical editor state with the following:

1
import {
2
convertMarkdownToLexical,
3
editorConfigFactory,
4
} from '@payloadcms/richtext-lexical'
5
6
const html = convertMarkdownToLexical({
7
editorConfig: await editorConfigFactory.default({
8
config, // <= make sure you have access to your Payload Config
9
}),
10
markdown: '# Hello world\n\nThis is a **test**.',
11
})

Converting MDX

Payload supports serializing and deserializing MDX content. While Markdown converters are stored on the features, MDX converters are stored on the blocks that you pass to the BlocksFeature.

Defining a Custom Block

Here is an example of a Banner block.

This block:

  • Renders in the admin UI as a normal Lexical block with specific fields (e.g. type, content).
  • Converts to an MDX Banner component.
  • Can parse that MDX Banner back into a Lexical state.
Shows the Banner field in a lexical editor and the MDX output
Banner field in a lexical editor and the MDX output
1
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
2
import type { Block, CollectionConfig, RichTextField } from 'payload'
3
4
import {
5
BlocksFeature,
6
convertLexicalToMarkdown,
7
editorConfigFactory,
8
lexicalEditor,
9
} from '@payloadcms/richtext-lexical'
10
11
const BannerBlock: Block = {
12
slug: 'Banner',
13
fields: [
14
{
15
name: 'type',
16
type: 'select',
17
defaultValue: 'info',
18
options: [
19
{ label: 'Info', value: 'info' },
20
{ label: 'Warning', value: 'warning' },
21
{ label: 'Error', value: 'error' },
22
],
23
},
24
{
25
name: 'content',
26
type: 'richText',
27
editor: lexicalEditor(),
28
},
29
],
30
jsx: {
31
/**
32
* Convert from Lexical -> MDX:
33
* <Banner type="..." >child content</Banner>
34
*/
35
export: ({ fields, lexicalToMarkdown }) => {
36
const props: any = {}
37
if (fields.type) {
38
props.type = fields.type
39
}
40
41
return {
42
children: lexicalToMarkdown({ editorState: fields.content }),
43
props,
44
}
45
},
46
/**
47
* Convert from MDX -> Lexical:
48
*/
49
import: ({ children, markdownToLexical, props }) => {
50
return {
51
type: props?.type,
52
content: markdownToLexical({ markdown: children }),
53
}
54
},
55
},
56
}
57
58
const Pages: CollectionConfig = {
59
slug: 'pages',
60
fields: [
61
{
62
name: 'nameOfYourRichTextField',
63
type: 'richText',
64
editor: lexicalEditor({
65
features: ({ defaultFeatures }) => [
66
...defaultFeatures,
67
BlocksFeature({
68
blocks: [BannerBlock],
69
}),
70
],
71
}),
72
},
73
{
74
name: 'markdown',
75
type: 'textarea',
76
hooks: {
77
afterRead: [
78
({ siblingData, siblingFields }) => {
79
const data: SerializedEditorState =
80
siblingData['nameOfYourRichTextField']
81
82
if (!data) {
83
return ''
84
}
85
86
const markdown = convertLexicalToMarkdown({
87
data,
88
editorConfig: editorConfigFactory.fromField({
89
field: siblingFields.find(
90
(field) =>
91
'name' in field && field.name === 'nameOfYourRichTextField',
92
) as RichTextField,
93
}),
94
})
95
96
return markdown
97
},
98
],
99
beforeChange: [
100
({ siblingData }) => {
101
// Ensure that the markdown field is not saved in the database
102
delete siblingData['markdown']
103
return null
104
},
105
],
106
},
107
},
108
],
109
}

The conversion is done using the jsx property of the block. The export function is called when converting from lexical to MDX, and the import function is called when converting from MDX to lexical.

Export

The export function takes the block field data and the lexicalToMarkdown function as arguments. It returns the following object:

Property

Type

Description

children

string

This will be in between the opening and closing tags of the block.

props

object

This will be in the opening tag of the block.

Import

The import function provides data extracted from the MDX. It takes the following arguments:

Argument

Type

Description

children

string

This will be the text between the opening and closing tags of the block.

props

object

These are the props passed to the block, parsed from the opening tag into an object.

The returning object is equal to the block field data.

Next

Converting Plaintext