lexicalHTML field doesn't update in client-side Live Preview
Link to reproduction
No response
Environment Info
Binaries:
Node: 20.9.0
npm: 10.1.0
Yarn: 1.22.19
pnpm: 9.7.1
Relevant Packages:
payload: 3.0.0-beta.107
next: 15.0.0-canary.136
@payloadcms/graphql: 3.0.0-beta.107
@payloadcms/live-preview: 3.0.0-beta.107
@payloadcms/live-preview-react: 3.0.0-beta.107
@payloadcms/next/utilities: 3.0.0-beta.107
@payloadcms/richtext-lexical: 3.0.0-beta.107
@payloadcms/translations: 3.0.0-beta.29
@payloadcms/ui/shared: 3.0.0-beta.107
react: 19.0.0-rc-e56f4ae3-20240830
react-dom: 19.0.0-rc-e56f4ae3-20240830
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 24.0.0: Mon Aug 12 20:52:41 PDT 2024; root:xnu-11215.1.10~2/RELEASE_ARM64_T6031
Available memory (MB): 36864
Available CPU cores: 14
Describe the Bug
When adding a rich text field and a lexicalHTML field for automatically converting it into HTML code, as well as enabling client-side Live Preview, only the rich text field content gets updated while typing, but not the generated HTML field.
Is this a bug or a design decision? If a bug, I could quickly create a repo for reproduction.
Reproduction Steps
- Create a collection with a rich text field
- Add a lexicalHTML field as documented
- Add client-side Live Preview as documented
Adapters and Plugins
No response
I'm also facing this issue and curious how it might be resolved; the experience for editing a lexicalHTML field is less than ideal in the current state of the live preview feature.
I guess you anyway know, but you could also switch to server-side live-preview together with Versions/Draft-mode and Autosave. Overall, I find this the even better experience.
@stephtr I was not aware of that option; I'll look into it. Thanks for the tip!
@stephtr to be clear, are you saying that we should see the generated HTML field update after an autosave? I tried setting up server-side live preview with versions/draft-mode and autosave, but I didn't see the generated HTML update in the live preview. I'm on an outdated version of the Payload beta, so that could be my issue, but I wasn't actually sure if you expected this to fix the problem.
facing the same issue on payload version: 2.30.1. I am guessing the afterRead hook that lexicalHTML uses behind the scenes is not re-running before the updated data is passed back to the live server client.
@alexnitta yes, for me that worked (I was running beta 107). You then either have about 2 seconds delay until autosave kicks in or decrease the autosave interval
Any updates on this? Server-side live preview is all good and well for frameworks with server-side components, but those of us using Vue3, for example, don't have that option.
This issue has been marked as stale due to lack of activity.
To keep this issue open, please indicate that it is still relevant in a comment below.
This issue was automatically closed due to lack of activity.
I'm facing same issue, the lexicalHTML is not callable on [slug]/page.ts .... steps:
fields: [ { name: "title", type: "text", required: true, label: "Post Title", hooks: { beforeValidate: [ ({ value, data, operation }: FieldHookArgs<PostType>) => { if (operation === "create" && data && (!data.slug || data.slug === "") && value) { data.slug = value .toLowerCase() .replace(/\s+/g, "-") .replace(/[^a-z0-9-]/g, ""); } return value; }, ], }, }, { name: "slug", type: "text", required: false, unique: false, admin: { description: "Auto-generated URL path", }, }, { name: "lexicalContent", type: "richText", editor: lexicalEditor({ features: ({ defaultFeatures }) => [ ...defaultFeatures, HTMLConverterFeature({}),
LinkFeature({
fields: ({ defaultFields }) => [
...defaultFields,
{
name: "rel",
label: "Rel Attribute",
type: "select",
hasMany: true,
options: ["noopener", "noreferrer", "nofollow"],
admin: {
description: "Define relationship attributes for links.",
},
},
],
}),
UploadFeature({
collections: {
uploads: {
fields: [
{
name: "caption",
type: "richText",
editor: lexicalEditor(),
},
],
},
},
}),
BlocksFeature({
blocks: [Banner, CallToAction, ImageGrid],
}),
],
}),
required: false,
label: "Post Content",
admin: {
description: "Add the main content of the post.",
},
},
lexicalHTML("lexicalContent", { name: "lexicalContentConverted_html" }),
--- Page.ts: // app/posts/[slug]/page.tsx
//@ts-ignore import { draftMode } from "next/headers"; //@ts-ignore import { notFound } from "next/navigation"; import { getPayload } from "payload"; import configPromise from "@payload-config"; import Posts from "../../../collections/Posts.ts";
import type { HTMLConverter } from "@payloadcms/richtext-lexical"; import { lexicalEditor } from "@payloadcms/richtext-lexical"; import { lexicalHTML } from "@payloadcms/richtext-lexical"; import { RefreshRouteOnSave } from "./RefreshRouteOnSave.tsx"; import LexicalContentRenderer from "@/components/LexicalHTMLRenderer.tsx";
async function getPost(slug: string, isDraft: boolean) { const payload = await getPayload({ config: configPromise });
const post = await payload.find({ collection: "posts", where: { slug: { equals: slug, }, }, draft: isDraft, // Fetch draft content if preview mode is enabled });
return post.docs[0]; }
export default async function PostPage({ params }: { params: { slug: string } }) { const { isEnabled } = await draftMode(); const { slug } = await params; console.log(slug, isEnabled); const post = await getPost(slug, isEnabled);
console.log(await post.title); console.log(await post.content); //const stingifiedContent = post.content;
//const postContent = JSON.stringify(post.content);
console.log("Post Content (Type):", typeof post.content); console.log( "Post Content (Value):", // <LexicalContentRenderer lexicalContent={post.lexicalContent} />, );
//console.log(lexicalHTML(lexicalContent, lexicalContentConverted_html)); ---> Want to call this, but unable to, please tell the way to call this function.
//const con = lexicalHTML("lexicalContent", { name: "lexicalContentConverted_html" }); // const stringifydContent = JSON.stringify(con); //console.log("Raw Lexical Content - lexicalContent (JSON) to String:", stringifydContent); // console.log("Raw Lexical Content - lexicalContent TYPE :", typeof stringifydContent);
console.log("Post Object:", post);
if (!post) { notFound(); }
// Parse the content field if it's a string let parsedContent; try { parsedContent = typeof post.content === "string" ? JSON.parse(post.content) : post.content; } catch (error) { console.error("Failed to parse content:", error); parsedContent = null; } console.log("Parsed Lexical Content:", parsedContent);
// console.log("Converted HTML:", post.lexicalContentConverted_html);
// Handle categories and tags const categories = Array.isArray(post.categories) ? post.categories : []; const tags = Array.isArray(post.tags) ? post.tags : [];
// Render the post content return ( <> <RefreshRouteOnSave /> <div style={{ padding: "20px", fontFamily: "Arial, sans-serif" }}>
{post.title}
Slug: {post.slug}
<div>
<strong>Content:</strong>{" "}
{parsedContent ? (
<LexicalContentRenderer content={parsedContent} />
) : (
<p>Failed to load content.</p>
)}
</div>
<p>
<strong>Excerpt:</strong> {post.excerpt || "No excerpt available."}
</p>
<p>
<strong>Author:</strong> {post.author || "Unknown"}
</p>
<p>
<strong>Status:</strong> {post.status}
</p>
<p>
<strong>Published Date:</strong> {post.publishedDate || "Not published yet."}
</p>
<p>
<strong>Post Type:</strong> {post.postType || "N/A"}
</p>
<p>
<strong>Visibility:</strong> {post.visibility || "Public"}
</p>
<p>
<strong>Categories:</strong>
{categories.length > 0
? categories.map((category: string, index: number) => (
<span key={index}>{category}</span>
))
: "None"}
</p>
<p>
<strong>Tags:</strong>
{tags.length > 0
? tags.map((tag: string | { name: string }, index: number) => (
<span key={index}>{typeof tag === "string" ? tag : tag.name}</span>
))
: "None"}
</p>
{post.featuredImage &&
typeof post.featuredImage === "object" &&
"url" in post.featuredImage && (
<div>
<strong>Featured Image:</strong>
<img
src={post.featuredImage.url || undefined}
alt="Featured"
style={{ maxWidth: "100%" }}
/>
</div>
)}
</div>
</>
); }
import React from "react";
interface LexicalContentRendererProps {
content: {
root: {
children: Array
const LexicalContentRenderer: React.FC<LexicalContentRendererProps> = ({ content }) => { const { root } = content;
if (!root || !Array.isArray(root.children)) { return
No content available.
; }return (
{child.children && child.children.map((textNode: any, textIndex: number) => { if (textNode.type === "text") { return {textNode.text}; } return null; })}
); } return null; })}export default LexicalContentRenderer;
/* import React from "react";
interface LexicalHTMLRendererProps { html: string; }
const LexicalHTMLRenderer: React.FC<LexicalHTMLRendererProps> = ({ html }) => { if (!html) { return
No content available.
; }return ( <div dangerouslySetInnerHTML={{ __html: html, }} /> ); };
export default LexicalHTMLRenderer; */
--whereas, I don't want to do all this, it's a headache to figure out a way to make this parsed correctly.
--> But when I try to call this on page.ts, it doesn't even recognize this, Don't even know how to call/pass this lexicalHTML, lexicalContentConverted_html to make it work, as this is not even recognized as props or anything like that. Please give code on how to use lexicalHTML, for live-preview in lexical format for the post.content field. Thank you. Waiting for reply eagerly.
P.S- also request you to update the Payload CMS documentation for lexicalHTML for live-preview, post.content field as an example, how to use HTMLConverters, as this is very very important, else we keep getting this error: payload Objects are not valid as a React child (found: object with keys {root}). If you meant to render a collection of children, use an array instead.
MongoDB: _id 67a800c8169258c4c1c82a1b parent 67a800c8169258c4c1c82a19
version Object title "krishna rama govinda" slug "krishna-rama-govinda"
lexicalContent Object
root Object
children Array (1)
0 Object
children Array (1)
0 Object detail 0 format 0 mode "normal" style "" text "ramamamram-RAM" type "text" version 1 direction "ltr" format "" indent 0 type "paragraph" version 1 textFormat 0 textStyle "" direction "ltr" format "" indent 0 type "root" version 1 lexicalContentConverted_html null
excerpt Object
root Object
children Array (1)
0 Object
children Array (1)
0 Object detail 0 format 0 mode "normal" style "" text "RAM" type "text" version 1 direction "ltr" format "" indent 0 type "paragraph" version 1 textFormat 0 textStyle "" direction "ltr" format "" indent 0 type "root" version 1
heroSliderImages Array (empty) status "draft" updatedAt 2025-02-09T01:11:36.620+00:00 createdAt 2025-02-09T01:11:36.620+00:00 _status "draft" createdAt 2025-02-09T01:11:36.892+00:00 updatedAt 2025-02-09T01:11:36.892+00:00 latest true __v 0
this is my output console logs: Access granted: Logged-in user GET /admin/collections/posts/67a68874a3650024fb5d834c/preview 200 in 5540ms om-sairam-swami--testing-content false Om Sairam Swami- Testing Content undefined Post Content (Type): undefined Post Content (Value): Post Object: { id: '67a68874a3650024fb5d834c', title: 'Om Sairam Swami- Testing Content', slug: 'om-sairam-swami--testing-content', lexicalContent: { root: { children: [Array], direction: 'ltr', format: '', indent: 0, type: 'root', version: 1 } }, lexicalContentConverted_html: '
Hare Rama Hare Rama Rama Rama Hare Hare
', excerpt: { root: { children: [Array], direction: 'ltr', format: '', indent: 0, type: 'root', version: 1 } }, heroSliderImages: [], postType: 'article', author: 'Developer', status: 'draft', publishedDate: '2025-02-08T11:30:00.000Z', _status: 'draft', createdAt: '2025-02-07T22:25:56.459Z', updatedAt: '2025-02-07T22:25:56.459Z' } Parsed Lexical Content: undefined ⨯ [Error: Objects are not valid as a React child (found: object with keys {root}). If you meant to render a collection of children, use an array instead.] { digest: '2907643195' } GET /posts/om-sairam-swami--testing-content?preview=true 500 in 4647msTHIS IS AN ISSUE, AS THIS FUNCTION LEXICALHTML IS NOT AVAILABLE FOR ARRAYS I GUESS, STILL, AS I UNDERSTAND FROM THIS DOCUMENTATION OF YOURS: https://payloadcms.com/community-help/discord/how-to-implement-lexical-to-html-converter-on-the-server (REQUEST YOUR ESTEEMED ASSISTANCE ASAP/ WORKAROUND CODE).
[Bug] Server-side Lexical to HTML serializer is not working for nested fields #4034 #4440 #4103
I am also facing this issue, however I don't have the option to switch to server-side live preview, because I am using Remix on the frontend. And I cannot use JSON, because the RichText response can contain lots and lots of deeply nested relation and link objects ... Would be cool, if client-side preview would enable this option, or maybe we can switch to JSON when doing preview and use HTML when doing preview
#> I am also facing this issue, however I don't have the option to switch to server-side live preview, because I am using Remix on the frontend. And I cannot use JSON, because the RichText response can contain lots and lots of deeply nested relation and link objects ... Would be cool, if client-side preview would enable this option, or maybe we can switch to JSON when doing preview and use HTML when doing preview
@HriBB try to do serialization of the fields where you are using lexicalEditor, that generates Lexical JSON, which will not accept Rich HTML format or strings, so do serialization for those payload fields such as content, title, excerpt, blocks, upload or whatever else you are using, then you can use those serialiazed fields on client side also or preview. Basically PayloadCMS should fix their HTMLConverter(), LexicalHTML(), so we are making workarounds by writing so much code, it is a lot of work. Steps for you: Define those fields in Collection Posts for example, as richText, LexicalEditor() (2) use hooks: beforeChange() -- write your logic to trigger conversion of lexicalJSON to HTML before saving to db 3) call it in [slug]/page.tsx NOTE: Caveat: it is not as easy, because you also need to write conversion logic like LexicalRender functions or RichTextRender whatever you can call it, then pass it in hooks, but i hope I've given you some idea, PLEASE DON'T MIND @PAYLOADCMS: I TRULY WISH, PAYLOAD CMS CAN IMPROVE THEIR DOCUMENTATION, GIVE WORKING EXAMPLES FOR EACH FEATURE THEY LAUNCH BY GIVING END TO END EXAMPLES ON GITHUB THAN LEAVING US FOR DOING HIT AND TRY PLAYING WITH EACH AND EVERY COMMAND OF PAYLOAD CMS. I TRIED TO CONTACT THEM VIA THEIR FORM AS NO PHONE NUMBER IS AVAILABLE; SOMEONE CONTACTED ME ON THIS TO GET MORE INFORMATION BUT THEY NEVER KEPT ME INFORMED ON THE STATUS FOR THIS OR PROVIDED WORKAROUND OR CONNECTED ME WITH THE TECH ON THIS. YOU CAN TRY TO CONTACT THEM ON THIS AS WELL.
The issue https://github.com/payloadcms/payload/issues/8168 describes the same problem from a more technical perspective.
I just wrote a comment there explaining how this is actually mostly a documentation issue and the recommended ways to resolve it. I'll soon improve the documentation with the information described in that comment.
Thanks everyone for your patience, and if you have any questions, let me know!
🚀 This is included in version v3.39.0
This issue has been automatically locked. Please open a new issue if this issue persists with any additional detail.
Fixed by https://github.com/payloadcms/payload/pull/13619 - the lexicalHTML field is now supported in client-side live preview!