diff --git a/docs/rich-text/converting-html.mdx b/docs/rich-text/converting-html.mdx index 024cbc57d6..60bc056ab4 100644 --- a/docs/rich-text/converting-html.mdx +++ b/docs/rich-text/converting-html.mdx @@ -6,14 +6,14 @@ desc: Converting between lexical richtext and HTML keywords: lexical, richtext, html --- -## Converting Rich Text to HTML +## Rich Text to HTML There are two main approaches to convert your Lexical-based rich text to HTML: 1. **Generate HTML on-demand (Recommended)**: Convert JSON to HTML wherever you need it, on-demand. 2. **Generate HTML within your Collection**: Create a new field that automatically converts your saved JSON content to HTML. This is not recommended because it adds overhead to the Payload API and may not work well with live preview. -### Generating HTML on-demand (Recommended) +### On-demand To convert JSON to HTML on-demand, use the `convertLexicalToHTML` function from `@payloadcms/richtext-lexical/html`. Here's an example of how to use it in a React component in your frontend: @@ -32,61 +32,81 @@ export const MyComponent = ({ data }: { data: SerializedEditorState }) => { } ``` -### Converting Lexical Blocks +#### Dynamic Population (Advanced) -If your rich text includes Lexical blocks, you need to provide a way to convert them to HTML. For example: +By default, `convertLexicalToHTML` expects fully populated data (e.g. uploads, links, etc.). If you need to dynamically fetch and populate those nodes, use the async variant, `convertLexicalToHTMLAsync`, from `@payloadcms/richtext-lexical/html-async`. You must provide a `populate` function: ```tsx 'use client' -import type { MyInlineBlock, MyTextBlock } from '@/payload-types' -import type { - DefaultNodeTypes, - SerializedBlockNode, - SerializedInlineBlockNode, -} from '@payloadcms/richtext-lexical' import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical' -import { - convertLexicalToHTML, - type HTMLConvertersFunction, -} from '@payloadcms/richtext-lexical/html' -import React from 'react' - -type NodeTypes = - | DefaultNodeTypes - | SerializedBlockNode - | SerializedInlineBlockNode - -const htmlConverters: HTMLConvertersFunction = ({ - defaultConverters, -}) => ({ - ...defaultConverters, - blocks: { - // Each key should match your block's slug - myTextBlock: ({ node, providedCSSString }) => - `
${node.fields.text}
`, - }, - inlineBlocks: { - // Each key should match your inline block's slug - myInlineBlock: ({ node, providedStyleTag }) => - `${node.fields.text}`, - }, -}) +import { getRestPopulateFn } from '@payloadcms/richtext-lexical/client' +import { convertLexicalToHTMLAsync } from '@payloadcms/richtext-lexical/html-async' +import React, { useEffect, useState } from 'react' export const MyComponent = ({ data }: { data: SerializedEditorState }) => { - const html = convertLexicalToHTML({ - converters: htmlConverters, - data, - }) + const [html, setHTML] = useState(null) + useEffect(() => { + async function convert() { + const html = await convertLexicalToHTMLAsync({ + data, + populate: getRestPopulateFn({ + apiURL: `http://localhost:3000/api`, + }), + }) + setHTML(html) + } - return
+ void convert() + }, [data]) + + return html &&
} ``` -### Outputting HTML from the Collection +Using the REST populate function will send a separate request for each node. If you need to populate a large number of nodes, this may be slow. For improved performance on the server, you can use the `getPayloadPopulateFn` function: -To automatically generate HTML from the saved richText field in your Collection, use the `lexicalHTMLField()` helper. This approach converts the JSON to HTML using an `afterRead` hook. For instance: +```tsx +import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical' + +import { getPayloadPopulateFn } from '@payloadcms/richtext-lexical' +import { convertLexicalToHTMLAsync } from '@payloadcms/richtext-lexical/html-async' +import { getPayload } from 'payload' +import React from 'react' + +import config from '../../config.js' + +export const MyRSCComponent = async ({ + data, +}: { + data: SerializedEditorState +}) => { + const payload = await getPayload({ + config, + }) + + const html = await convertLexicalToHTMLAsync({ + data, + populate: await getPayloadPopulateFn({ + currentDepth: 0, + depth: 1, + payload, + }), + }) + + return html &&
+} +``` + +### HTML field + +The `lexicalHTMLField()` helper converts JSON to HTML and saves it in a field that is updated every time you read it via an `afterRead` hook. It's generally not recommended for two reasons: + +1. It creates a column with duplicate content in another format. +2. In [client-side live preview](/docs/live-preview/client), it makes it not "live". + +Consider using the [on-demand HTML converter above](/docs/rich-text/converting-html#on-demand-recommended) or the [JSX converter](/docs/rich-text/converting-jsx) unless you have a good reason. ```ts import type { HTMLConvertersFunction } from '@payloadcms/richtext-lexical/html' @@ -154,74 +174,59 @@ const Pages: CollectionConfig = { } ``` -### Generating HTML in Your Frontend with Dynamic Population (Advanced) +## Blocks to HTML -By default, `convertLexicalToHTML` expects fully populated data (e.g. uploads, links, etc.). If you need to dynamically fetch and populate those nodes, use the async variant, `convertLexicalToHTMLAsync`, from `@payloadcms/richtext-lexical/html-async`. You must provide a `populate` function: +If your rich text includes Lexical blocks, you need to provide a way to convert them to HTML. For example: ```tsx 'use client' +import type { MyInlineBlock, MyTextBlock } from '@/payload-types' +import type { + DefaultNodeTypes, + SerializedBlockNode, + SerializedInlineBlockNode, +} from '@payloadcms/richtext-lexical' import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical' -import { getRestPopulateFn } from '@payloadcms/richtext-lexical/client' -import { convertLexicalToHTMLAsync } from '@payloadcms/richtext-lexical/html-async' -import React, { useEffect, useState } from 'react' - -export const MyComponent = ({ data }: { data: SerializedEditorState }) => { - const [html, setHTML] = useState(null) - useEffect(() => { - async function convert() { - const html = await convertLexicalToHTMLAsync({ - data, - populate: getRestPopulateFn({ - apiURL: `http://localhost:3000/api`, - }), - }) - setHTML(html) - } - - void convert() - }, [data]) - - return html &&
-} -``` - -Using the REST populate function will send a separate request for each node. If you need to populate a large number of nodes, this may be slow. For improved performance on the server, you can use the `getPayloadPopulateFn` function: - -```tsx -import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical' - -import { getPayloadPopulateFn } from '@payloadcms/richtext-lexical' -import { convertLexicalToHTMLAsync } from '@payloadcms/richtext-lexical/html-async' -import { getPayload } from 'payload' +import { + convertLexicalToHTML, + type HTMLConvertersFunction, +} from '@payloadcms/richtext-lexical/html' import React from 'react' -import config from '../../config.js' +type NodeTypes = + | DefaultNodeTypes + | SerializedBlockNode + | SerializedInlineBlockNode -export const MyRSCComponent = async ({ - data, -}: { - data: SerializedEditorState -}) => { - const payload = await getPayload({ - config, - }) +const htmlConverters: HTMLConvertersFunction = ({ + defaultConverters, +}) => ({ + ...defaultConverters, + blocks: { + // Each key should match your block's slug + myTextBlock: ({ node, providedCSSString }) => + `
${node.fields.text}
`, + }, + inlineBlocks: { + // Each key should match your inline block's slug + myInlineBlock: ({ node, providedStyleTag }) => + `${node.fields.text}`, + }, +}) - const html = await convertLexicalToHTMLAsync({ +export const MyComponent = ({ data }: { data: SerializedEditorState }) => { + const html = convertLexicalToHTML({ + converters: htmlConverters, data, - populate: await getPayloadPopulateFn({ - currentDepth: 0, - depth: 1, - payload, - }), }) - return html &&
+ return
} ``` -## Converting HTML to Richtext +## HTML to Richtext If you need to convert raw HTML into a Lexical editor state, use `convertHTMLToLexical` from `@payloadcms/richtext-lexical`, along with the [editorConfigFactory to retrieve the editor config](/docs/rich-text/converters#retrieving-the-editor-config): diff --git a/docs/rich-text/converting-jsx.mdx b/docs/rich-text/converting-jsx.mdx index 48462aac5d..254a31db38 100644 --- a/docs/rich-text/converting-jsx.mdx +++ b/docs/rich-text/converting-jsx.mdx @@ -6,7 +6,7 @@ desc: Converting between lexical richtext and JSX keywords: lexical, richtext, jsx --- -## Converting Richtext to JSX +## Richtext to JSX To convert richtext to JSX, import the `RichText` component from `@payloadcms/richtext-lexical/react` and pass the richtext content to it: @@ -28,7 +28,7 @@ The `RichText` component includes built-in converters for common Lexical nodes. populated data to work correctly. -### Converting Internal Links +### Internal Links By default, Payload doesn't know how to convert **internal** links to JSX, as it doesn't know what the corresponding URL of the internal link is. You'll notice that you get a "found internal link, but internalDocToHref is not provided" error in the console when you try to render content with internal links. @@ -81,7 +81,7 @@ export const MyComponent: React.FC<{ } ``` -### Converting Lexical Blocks +### Lexical Blocks If your rich text includes custom Blocks or Inline Blocks, you must supply custom converters that match each block's slug. This converter is not included by default, as Payload doesn't know how to render your custom blocks. @@ -133,7 +133,7 @@ export const MyComponent: React.FC<{ } ``` -### Overriding Default JSX Converters +### Overriding Converters You can override any of the default JSX converters by passing passing your custom converter, keyed to the node type, to the `converters` prop / the converters function. diff --git a/docs/rich-text/converting-markdown.mdx b/docs/rich-text/converting-markdown.mdx index c34047abb6..2b4b43efd0 100644 --- a/docs/rich-text/converting-markdown.mdx +++ b/docs/rich-text/converting-markdown.mdx @@ -6,7 +6,7 @@ desc: Converting between lexical richtext and Markdown / MDX keywords: lexical, richtext, markdown, md, mdx --- -## Converting Richtext to Markdown +## Richtext to Markdown If you have access to the Payload Config and the [lexical editor config](/docs/rich-text/converters#retrieving-the-editor-config), you can convert the lexical editor state to Markdown with the following: @@ -91,7 +91,7 @@ const Pages: CollectionConfig = { } ``` -## Converting Markdown to Richtext +## Markdown to Richtext If you have access to the Payload Config and the [lexical editor config](/docs/rich-text/converters#retrieving-the-editor-config), you can convert Markdown to the lexical editor state with the following: diff --git a/docs/rich-text/converting-plaintext.mdx b/docs/rich-text/converting-plaintext.mdx index b3ea50697b..4f22a731ae 100644 --- a/docs/rich-text/converting-plaintext.mdx +++ b/docs/rich-text/converting-plaintext.mdx @@ -6,7 +6,7 @@ desc: Converting between lexical richtext and plaintext keywords: lexical, richtext, plaintext, text --- -## Converting Richtext to Plaintext +## Richtext to Plaintext Here's how you can convert richtext data to plaintext using `@payloadcms/richtext-lexical/plaintext`.