this is my payload.config.ts
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
HTMLConverterFeature({}),
BlocksFeature({
blocks: [AlertBlock, AlignmentBlock, YoutubeBlock, CodeBlock],
}),
],
}),Now in my frontend i get this data back
content_html: '<h1>H1</h1><h2>H2</h2><h3>H3</h3><h3>H4</h3><h4>H5</h4><h6>H6</h6><span>unknown node</span><blockquote>QUOTE</blockquote><img src="/media/minecraft.png" alt="minecraft.png" width="500" height="500"/><ul class="bullet"><li value=1>Unordered List x1</li><li value=2>Unordered List x2</li></ul><ol class="number"><li value=1> Ordered List x1</li><li value=2>Ordered List x2</li></ol><ul class="check"><li aria-checked=false class="list-item-checkbox-checked"\n' +
' role="checkbox"\n' +
' tabIndex=-1\n' +
' value=1\n' +
' >\n' +
' {serializedChildren}\n' +
' </li></ul><span>unknown node</span><ul class="check"></ul><span>unknown node</span><span>unknown node</span><span>unknown node</span><p>Paragraph </p><p></p><h1>UL with a code block</h1><span>unknown node</span><h1></h1>'1)All the unknown nodes are the custom blocks, example for one of them is
import { Block } from "payload/types";
export const YoutubeBlock: Block = {
slug: "Youtube",
interfaceName: "YoutubeBlock",
fields: [
{
name: "link",
type: "text",
required: true,
admin: {
description: "The embed link of the youtube video",
},
},
],
};So how do i make the unknown nodes.. not unknown and have actual data
2) Is there a better way to parse the html, instead of getting a massive html string?
Bump
2) Is there a better way to parse the html, instead of getting a massive html string?
Absolutely. The HTML generation is mainly for lazy people who want to generate the HTML.
Meaning you generate it in payload / wherever you want, and display it. Nothing more.
If you want to do transformations / any custom stuff OUTSIDE of customizing an HTML converter, you should traverse the editor JSON yourself (JSON is far nicer to work with than HTML for that, and you get more data)
Here is an example for traversing HTML. For JSX (React) you'd simply have to make it a component and return JSX instead of a HTML string
You can also find an (older) JSX example here, however I personally prefer the pattern where each converter = one file. It's cleaner
Converting the richText on the client instead of payload also gives you one of the main strengths of a headless cms compared to a regular cms, namely your data isn't coupled to a UI layer anymore. Meaning you can use the same dataset for both web and mobile apps (or other frontend implementations).
Intresting.. would you have an example for a rich text field with custom blocks in it?
quic question but you are returning html strings right?
Would you happen to know if it's possible to return jsx elements instead?
You query the richText content as json from the payload api. You can then convert the json to jsx when your client is nextjs. Rendering blocks is basically just adding extra cases to the sample code Alessio linked
I have been doing some thinking
Couldn't this be an array? And each converter returns jsx?
Yall can mark this solved.... the json thing by Alessio worked awesome
Do you have any example or plugin that you recommend?
The e-commerce template has pretty decent sample code showing it
serialize.tsx:
const serialize = (children?: Children): React.ReactNode[] =>
children?.map((node, i) => {
if (Text.isText(node)) {
let text = <span dangerouslySetInnerHTML={{ __html: escapeHTML(node.text) }} />
if (node.bold) {
text = <strong key={i}>{text}</strong>
}
if (node.code) {
text = <code key={i}>{text}</code>
}
if (node.italic) {
text = <em key={i}>{text}</em>
}
if (node.underline) {
text = (
<span style={{ textDecoration: 'underline' }} key={i}>
{text}
</span>
)
}
if (node.strikethrough) {
text = (
<span style={{ textDecoration: 'line-through' }} key={i}>
{text}
</span>
)
}
return <Fragment key={i}>{text}</Fragment>
}
if (!node) {
return null
}
switch (node.type) {
case 'h1':
return <h1 key={i}>{serialize(node?.children)}</h1>
case 'h2':
return <h2 key={i}>{serialize(node?.children)}</h2>
case 'h3':
return <h3 key={i}>{serialize(node?.children)}</h3>
case 'h4':
return <h4 key={i}>{serialize(node?.children)}</h4>
case 'h5':
return <h5 key={i}>{serialize(node?.children)}</h5>
case 'h6':
return <h6 key={i}>{serialize(node?.children)}</h6>
case 'quote':
return <blockquote key={i}>{serialize(node?.children)}</blockquote>
case 'ul':
return <ul key={i}>{serialize(node?.children)}</ul>
case 'ol':
return <ol key={i}>{serialize(node.children)}</ol>
case 'li':
return <li key={i}>{serialize(node.children)}</li>
case 'link':
...I'm more or less doing it the same way. My use case at the moment is pretty simple though. I might make a converter per node type and put them in separate files like Alessio suggested when it grows bigger.
which ecommerce template?
It's one of the options you can select when running
npx create-payload-appOk, interesting!
Star
Discord
online
Get dedicated engineering support directly from the Payload team.