diff --git a/app/components/RichTextEditor.tsx b/app/components/RichTextEditor.tsx index c9f9766..9027f41 100644 --- a/app/components/RichTextEditor.tsx +++ b/app/components/RichTextEditor.tsx @@ -58,6 +58,12 @@ export function RichTextEditor({ const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); + // Mirror editor HTML into local state so the hidden always + // reflects the latest content. Without this, React doesn't re-render + // when TipTap's content changes and the form submits stale HTML. + const initialHtml = swapCidToLogo(defaultValue || "
", logoDataUrl); + const [html, setHtml] = useState(initialHtml); + const editor = useEditor({ immediatelyRender: false, extensions: [ @@ -85,6 +91,9 @@ export function RichTextEditor({ Color, ], content: swapCidToLogo(defaultValue || "", logoDataUrl), + onUpdate: ({ editor }) => { + setHtml(editor.getHTML()); + }, editorProps: { attributes: { class: "wysiwyg-editor", @@ -96,7 +105,7 @@ export function RichTextEditor({ // Keep editor disposed cleanly on unmount. useEffect(() => () => editor?.destroy(), [editor]); - const html = swapLogoToCid(editor?.getHTML() ?? defaultValue, logoDataUrl); + const submittedHtml = swapLogoToCid(html, logoDataUrl); if (!mounted) { // Server / pre-hydration fallback: a textarea so the value is still @@ -219,7 +228,7 @@ export function RichTextEditor({ />