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.

Rendering On Demand

Lexical in Payload is a React Server Component (RSC). Historically that created three headaches: 1. You couldn't render the editor directly from the client. 2. Features like blocks, tables and link drawers require the server to know the shape of nested sub-fields at render time. If you tried to render on demand, the server didn't know those schemas. 3. The rich text field is designed to live inside a Form. For simple use cases, setting up a full form just to manage editor state was cumbersome.

To simplify rendering richtext on demand, <RenderLexical />, that renders a Lexical editor while still covering the full feature set. On mount, it calls a server action to render the editor on the server using the new render-field server function. That server render gives Lexical everything it needs (including nested field schemas) and returns a ready-to-hydrate editor.

Inside an existing Form

If you have an existing Form and want to render a richtext field within it, you can use the RenderLexical component like this:

1
'use client'
2
3
import type { JSONFieldClientComponent } from 'payload'
4
5
import {
6
buildEditorState,
7
RenderLexical,
8
} from '@payloadcms/richtext-lexical/client'
9
10
import { lexicalFullyFeaturedSlug } from '../../slugs.js'
11
12
export const Component: JSONFieldClientComponent = (args) => {
13
return (
14
<RenderLexical
15
field={{
16
name: 'myFieldName' /* Make sure this matches the field name present in your form */,
17
}}
18
initialValue={buildEditorState({ text: 'default value' })}
19
schemaPath={`collection.${lexicalFullyFeaturedSlug}.richText`}
20
/>
21
)
22
}

Outside of a Form (you control state)

1
'use client'
2
3
import type { DefaultTypedEditorState } from '@payloadcms/richtext-lexical'
4
import type { JSONFieldClientComponent } from 'payload'
5
6
import {
7
buildEditorState,
8
RenderLexical,
9
} from '@payloadcms/richtext-lexical/client'
10
import React, { useState } from 'react'
11
12
import { lexicalFullyFeaturedSlug } from '../../slugs.js'
13
14
export const Component: JSONFieldClientComponent = (args) => {
15
// Manually manage the editor state
16
const [value, setValue] = useState<DefaultTypedEditorState | undefined>(() =>
17
buildEditorState({ text: 'state default' }),
18
)
19
20
const handleReset = React.useCallback(() => {
21
setValue(buildEditorState({ text: 'state default' }))
22
}, [])
23
24
return (
25
<div>
26
<RenderLexical
27
field={{ name: 'myField' }}
28
initialValue={buildEditorState({ text: 'default value' })}
29
schemaPath={`collection.${lexicalFullyFeaturedSlug}.richText`}
30
setValue={setValue as any}
31
value={value}
32
/>
33
<button onClick={handleReset} style={{ marginTop: 8 }} type="button">
34
Reset Editor State
35
</button>
36
</div>
37
)
38
}

Choosing the schemaPath

schemaPath tells the server which richText field to render. This gives the server the exact nested field schemas (blocks, relationship drawers, upload fields, tables, etc.).

Format:

  • collection.<collectionSlug>.<fieldPath>
  • global.<globalSlug>.<fieldPath>

Example (top level): collection.posts.richText

Example (nested in a group/tab): collection.posts.content.richText

Next

Lexical Migration