Rich Text to JSX
ImportRichText from the React subpath and pass it a serialized editor state:
import type { SerializedEditorState } from '@frogbotai/richtext-lexical/lexical'
import { RichText } from '@frogbotai/richtext-lexical/react'
export function ArticleBody({ data }: { data: SerializedEditorState }) {
return <RichText data={data} />
}
| Prop | Type | Default | Description |
|---|---|---|---|
data | SerializedEditorState | Required | The rich text value to render. An empty editor state renders an empty wrapping div; a missing value renders nothing. |
converters | JSXConverters | ({ defaultConverters }) => JSXConverters | Built-in converters | Converters keyed by node type, with custom blocks under blocks and inlineBlocks keyed by slug, and an optional unknown fallback. An object replaces the built-in converters entirely; a function receives them as defaultConverters so you can extend them. |
nodeMap | LexicalEditorNodeMap | None | A view’s node map, such as postViews.preview.nodes. Its entries override converters for the same node type or block slug. See Views. |
className | string | Built-in class name | Class name of the wrapping div. |
disableContainer | boolean | false | Renders the content without the wrapping div. |
disableIndent | boolean | string[] | false | Omits indentation styles. true disables them for every node; an array disables them for the listed node types. |
disableTextAlign | boolean | string[] | false | Omits text-alignment styles. true disables them for every node; an array disables them for the listed node types. |
Internal links
FrogBot cannot infer your application’s route for a linked record. AddLinkJSXConverter with an internalDocToHref function:
import type {
DefaultNodeTypes,
SerializedLinkNode,
} from '@frogbotai/richtext-lexical'
import type { SerializedEditorState } from '@frogbotai/richtext-lexical/lexical'
import {
type JSXConvertersFunction,
LinkJSXConverter,
RichText,
} from '@frogbotai/richtext-lexical/react'
const internalDocToHref = ({ linkNode }: { linkNode: SerializedLinkNode }) => {
const { relationTo, value } = linkNode.fields.doc!
if (typeof value !== 'object') {
throw new Error('Expected a populated internal link')
}
if (relationTo === 'pages') {
return `/${value.slug}`
}
return `/${relationTo}/${value.slug}`
}
const converters: JSXConvertersFunction<DefaultNodeTypes> = ({
defaultConverters,
}) => ({
...defaultConverters,
...LinkJSXConverter({ internalDocToHref }),
})
export function ArticleBody({ data }: { data: SerializedEditorState }) {
return <RichText<DefaultNodeTypes> converters={converters} data={data} />
}
LinkJSXConverter returns converters for the link and autolink node types.
| Option | Type | Default | Description |
|---|---|---|---|
internalDocToHref | ({ linkNode }) => string | None | Returns the URL for an internal link from its serialized node; the linked record is in linkNode.fields.doc. Without it, internal links render with href="#" and an error is logged. |
Lexical blocks
Custom blocks need converters keyed by block slug:'use client'
import type {
DefaultNodeTypes,
SerializedBlockNode,
SerializedInlineBlockNode,
} from '@frogbotai/richtext-lexical'
import type { SerializedEditorState } from '@frogbotai/richtext-lexical/lexical'
import {
type JSXConvertersFunction,
RichText,
} from '@frogbotai/richtext-lexical/react'
type BadgeBlock = {
blockName?: string
blockType: 'badge'
label: string
}
type NumberBlock = {
blockName?: string
blockType: 'number'
number: number
}
type TextBlock = {
blockName?: string
blockType: 'text'
text: string
}
type NodeTypes =
| DefaultNodeTypes
| SerializedBlockNode<NumberBlock | TextBlock>
| SerializedInlineBlockNode<BadgeBlock>
const converters: JSXConvertersFunction<NodeTypes> = ({
defaultConverters,
}) => ({
...defaultConverters,
blocks: {
number: ({ node }) => <div>{node.fields.number}</div>,
text: ({ node }) => <div>{node.fields.text}</div>,
},
inlineBlocks: {
badge: ({ node }) => <span>{node.fields.label}</span>,
},
})
export function ArticleBody({ data }: { data: SerializedEditorState }) {
return <RichText converters={converters} data={data} />
}
Override a converter
Return a converter under the standard node key to replace its default rendering. This example renders file images withnext/image:
'use client'
import type {
DefaultNodeTypes,
SerializedUploadNode,
} from '@frogbotai/richtext-lexical'
import type { SerializedEditorState } from '@frogbotai/richtext-lexical/lexical'
import {
type JSXConvertersFunction,
RichText,
} from '@frogbotai/richtext-lexical/react'
import Image from 'next/image'
const converters: JSXConvertersFunction<DefaultNodeTypes> = ({
defaultConverters,
}) => ({
...defaultConverters,
upload: ({ node }: { node: SerializedUploadNode }) => {
const file = node.value
if (node.relationTo !== 'files' || !file || typeof file !== 'object' || !file.url) {
return null
}
return (
<Image
alt={file.alt ?? ''}
height={file.height ?? 0}
src={file.url}
width={file.width ?? 0}
/>
)
},
})
export function ArticleBody({ data }: { data: SerializedEditorState }) {
return <RichText converters={converters} data={data} />
}