Open bfaulk96 opened 11 months ago
Is there any update?
Same error. Here is the screenshot.
No ETA until consulting request.
I'm having the same issue.
Having the issue too
@zbeyens Any solution?
After thorough debugging over several hours, it was determined that the issue arose from the inclusion of the createBlockSelectionPlugin with specific options, notably:
createBlockSelectionPlugin({ options: { sizes: { top: 0, bottom: 0, }, }, })
The solution involved filtering the array of plugins to exclude the blockSelection plugin:
const excludedSelectionPlugin = plugins?.filter(plugin => plugin?.key !== 'blockSelection');
Subsequently, the modified array excludedSelectionPlugin was passed to the createPlateEditor function:
serializeHtml(createPlateEditor({ plugins: excludedSelectionPlugin }), { nodes: textEditor, data, dndWrapper: props => <DndProvider backend={HTML5Backend} {...props} />, })
This adjustment successfully resolved the encountered issue.
Hope you will figure out the original issue which was caused by 'createBlockSelectionPlugin';
@andshonia Thank you. Your solution works in my case.
I have the same issue but the workaround is not working for me. It appears that other plugins might also have bug which is causing invalid hook call. Issue happens inside the serializeHtml() call.
Here have the error message call serializeHtml function. how can I do to resolve this error?
Invalid hook call. Hooks can only be called inside of the body of a function component.
After thorough debugging over several hours, it was determined that the issue arose from the inclusion of the createBlockSelectionPlugin with specific options, notably:
createBlockSelectionPlugin({ options: { sizes: { top: 0, bottom: 0, }, }, })
The solution involved filtering the array of plugins to exclude the blockSelection plugin:
const excludedSelectionPlugin = plugins?.filter(plugin => plugin?.key !== 'blockSelection');
Subsequently, the modified array excludedSelectionPlugin was passed to the createPlateEditor function:
serializeHtml(createPlateEditor({ plugins: excludedSelectionPlugin }), { nodes: textEditor, data, dndWrapper: props => <DndProvider backend={HTML5Backend} {...props} />, })
This adjustment successfully resolved the encountered issue.
Hope you will figure out the original issue which was caused by 'createBlockSelectionPlugin';
I find out, that your solution is not working for me, and made some debugging. now I can See, that createTogglePlugin
also causes this bug.
I was also facing the same issue:
Workaround (that worked for me):
createPlateEditor({ plugins: plugins?.filter( (plugin) => plugin?.key !== 'toggle' && plugin?.key !== 'blockSelection' ), })
By filter out the toggle
and blockSelection
, the html serialization is now working for me. Furthermore, I do suggest adding context={window}
on DndWrapper
according to this comment: Multiple DndProviders inside a pure component can lead to Cannot have two HTML5 backends at the same time #3257, if anyone faces cannot have two HTML5Backend
problem from DnDWrapper
.
for me filtering toggle
and blockSelection
did not work. i needed to filter p
also
const filteredPlugins = plugins?.filter((plugin) => plugin?.key !== 'toggle' && plugin?.key !== 'blockSelection' && plugin?.key !== 'p');
Latest v34
for me filtering
toggle
andblockSelection
did not work. i needed to filterp
also
const filteredPlugins = plugins?.filter((plugin) => plugin?.key !== 'toggle' && plugin?.key !== 'blockSelection' && plugin?.key !== 'p');
Latest v34
I removed the p filtering now it works without it!!!!
But i am facing another problem, i am desearilizing a html and showing it in the editor, but if there is any text, (p tag), the editor get reset if i type something
By creating a temporary editor and replacing serializeHtml with htmlReact, this solution worked for me
<Plate
editor={editor}
onChange={() => {
const tmp = createPlateEditor({
plugins: [HtmlReactPlugin],
})
const html= tmp.api.htmlReact.serialize({ nodes: editor.children })
console.log(html)
}}
>
<FixedToolbar>
<FixedToolbarButtons />
</FixedToolbar>
<Editor />
<FloatingToolbar>
<FloatingToolbarButtons />
</FloatingToolbar>
<CommentsPopover />
</Plate>
By creating a temporary editor and replacing serializeHtml with htmlReact, this solution worked for me
<Plate editor={editor} onChange={() => { const tmp = createPlateEditor({ plugins: [HtmlReactPlugin], }) const html= tmp.api.htmlReact.serialize({ nodes: editor.children }) console.log(html) }} > <FixedToolbar> <FixedToolbarButtons /> </FixedToolbar> <Editor /> <FloatingToolbar> <FloatingToolbarButtons /> </FloatingToolbar> <CommentsPopover /> </Plate>
This worked for me! Lifesaver took me 2 days and your comment saved me.
You're omitting the components so this would serialize to default div and span without any styles. The workaround to above errors is to pass your own context-free components this way:
const tmp = createPlateEditor({
plugins: [HtmlReactPlugin],
override: {
components: {
p: HtmlParagraphElement,
// ...
}
}
})
I solved it by blocking the BlockSelectionPlugin
// BlockSelectionPlugin,
Doing like this doesn't it mean opting out the BlockSelectionPlugin
feature on your editor? IMO, we only wanted to filter out the selection when the html serialization is needed.
We'll work on read-only components compatible with this serializer, that will also be useful for read-only editors.
Put editor.api.htmlReact.serialize({nodes:editor.children}) in a separate function. as it can't be use inside Plate's onChange handler.
Just a quick code for better understanding!, Hope this will help!
// PLATE COMPONENTS
import { Editor } from "@/components/plate-ui/Editor";
import { FixedToolbar } from "@/components/plate-ui/FixedToolbar";
import { FixedToolbarButtons } from "@/components/plate-ui/fixed-toolbar-buttons";
import { TooltipProvider } from "@/components/plate-ui/Tooltip";
import { useCreateEditor } from "@/hooks/useCreateEditor";
import { useEffect, useRef, useState } from "react";
import { Plate } from "@udecode/plate-common/react";
export const EditorComponent = () => {
const [content, setContent] = useState<any>("");
const [_html, setHTML] = useState<any>("");
const [isClicked, setIsClicked] = useState<boolean>(false);
const containerRef = useRef(null);
const editor = useCreateEditor();
useEffect(() => {
console.log("CONTENT", content);
}, [content, _html]);
const converToHtml = () => {
const html = editor.api.htmlReact.serialize({
nodes: content,
stripDataAttributes: false,
stripWhitespace: false,
// preserveClassNames: ["slate-h1", "slate-h2", "slate-h3"],
});
setHTML(html);
console.log("Serialized HTML:", html);
setIsClicked(true);
console.log("HTML", _html);
};
return (
<>
<Plate
editor={editor}
onChange={({ value }) => {
setContent(value);
}}
>
<FixedToolbar className="sticky">
<FixedToolbarButtons />
</FixedToolbar>
<TooltipProvider>
<Editor ref={containerRef} className="h-auto" />
</TooltipProvider>
</Plate>
<button
className="border bg-purple-900 text-white text-2xl rounded-md p-2"
onClick={converToHtml}
>
convert to HTML
</button>
{isClicked ? (
<div
className="w-full border border-black h-[100vh]"
dangerouslySetInnerHTML={{ __html: _html }}
/>
) : null}
</>
);
};
Could a kind soul give me some additional information on this useEditorRef must be used inside a Plate or PlateController
problem?
I would like to revive the topic: I would like to render the HTML in relation to what was written, and if possible without a temporary editor (which is not, in my opinion, a viable solution).
block-selection.tsx
-> block-selection-html.tsx
editorHtml.api.htmlReact.serialize
should workThis requires some effort. We'll abstract this as soon as we can.
@pirmax
Well, you can try to wrap up your editor component with Hoc. Like: const Editor = withHoc(Plate controller, ()=>{...your code}) Hopes, it will not throw an error.
You're omitting the components so this would serialize to default div and span without any styles. The workaround to above errors is to pass your own context-free components this way:
const tmp = createPlateEditor({ plugins: [HtmlReactPlugin], override: { components: { p: HtmlParagraphElement, // ... } } })
I'm doing it like this as well, but I get plain divs with none of the applied styles:
<div>Playground</div><div class="slate-p">A rich-text editor with AI capabilities. Try the AI commands or use Cmd+J to open the AI menu.</div>
const tmpEditor = createPlateEditor({
plugins: [HtmlReactPlugin],
override: {
components: editorComponents,
},
})
const html = tmpEditor.api.htmlReact.serialize({
nodes: editor.children,
convertNewLinesToHtmlBr: true,
stripWhitespace: false,
dndWrapper: (props) => (
<DndProvider backend={HTML5Backend} {...props} />
),
});
export const editorComponents = {
// [AIPlugin.key]: AILeaf,
[BlockquotePlugin.key]: BlockquoteElement,
[BoldPlugin.key]: withProps(PlateLeaf, { as: "strong" }),
[CodeBlockPlugin.key]: CodeBlockElement,
[CodeLinePlugin.key]: CodeLineElement,
[CodePlugin.key]: CodeLeaf,
[CodeSyntaxPlugin.key]: CodeSyntaxLeaf,
[ColumnItemPlugin.key]: ColumnElement,
[ColumnPlugin.key]: ColumnGroupElement,
[CommentsPlugin.key]: CommentLeaf,
[DatePlugin.key]: DateElement,
[EmojiInputPlugin.key]: EmojiInputElement,
[HEADING_KEYS.h1]: withProps(HeadingElement, { variant: "h1" }),
[HEADING_KEYS.h2]: withProps(HeadingElement, { variant: "h2" }),
[HEADING_KEYS.h3]: withProps(HeadingElement, { variant: "h3" }),
[HEADING_KEYS.h4]: withProps(HeadingElement, { variant: "h4" }),
[HEADING_KEYS.h5]: withProps(HeadingElement, { variant: "h5" }),
[HEADING_KEYS.h6]: withProps(HeadingElement, { variant: "h6" }),
[HighlightPlugin.key]: HighlightLeaf,
[HorizontalRulePlugin.key]: HrElement,
[ImagePlugin.key]: ImageElement,
[ItalicPlugin.key]: withProps(PlateLeaf, { as: "em" }),
[KbdPlugin.key]: KbdLeaf,
[LinkPlugin.key]: LinkElement,
[MediaEmbedPlugin.key]: MediaEmbedElement,
[MentionInputPlugin.key]: MentionInputElement,
[MentionPlugin.key]: MentionElement,
[ParagraphPlugin.key]: ParagraphElement,
[SlashInputPlugin.key]: SlashInputElement,
[StrikethroughPlugin.key]: withProps(PlateLeaf, { as: "s" }),
[SubscriptPlugin.key]: withProps(PlateLeaf, { as: "sub" }),
[SuperscriptPlugin.key]: withProps(PlateLeaf, { as: "sup" }),
[TableCellHeaderPlugin.key]: TableCellHeaderElement,
[TableCellPlugin.key]: TableCellElement,
[TablePlugin.key]: TableElement,
[TableRowPlugin.key]: TableRowElement,
[TocPlugin.key]: TocElement,
[TogglePlugin.key]: ToggleElement,
[UnderlinePlugin.key]: withProps(PlateLeaf, { as: "u" }),
};
You're omitting the components so this would serialize to default div and span without any styles. The workaround to above errors is to pass your own context-free components this way:
const tmp = createPlateEditor({ plugins: [HtmlReactPlugin], override: { components: { p: HtmlParagraphElement, // ... } } })
I'm doing it like this as well, but I get plain divs with none of the applied styles:
<div>Playground</div><div class="slate-p">A rich-text editor with AI capabilities. Try the AI commands or use Cmd+J to open the AI menu.</div>
const tmpEditor = createPlateEditor({ plugins: [HtmlReactPlugin], override: { components: editorComponents, }, }) const html = tmpEditor.api.htmlReact.serialize({ nodes: editor.children, convertNewLinesToHtmlBr: true, stripWhitespace: false, dndWrapper: (props) => ( <DndProvider backend={HTML5Backend} {...props} /> ), }); export const editorComponents = { // [AIPlugin.key]: AILeaf, [BlockquotePlugin.key]: BlockquoteElement, [BoldPlugin.key]: withProps(PlateLeaf, { as: "strong" }), [CodeBlockPlugin.key]: CodeBlockElement, [CodeLinePlugin.key]: CodeLineElement, [CodePlugin.key]: CodeLeaf, [CodeSyntaxPlugin.key]: CodeSyntaxLeaf, [ColumnItemPlugin.key]: ColumnElement, [ColumnPlugin.key]: ColumnGroupElement, [CommentsPlugin.key]: CommentLeaf, [DatePlugin.key]: DateElement, [EmojiInputPlugin.key]: EmojiInputElement, [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: "h1" }), [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: "h2" }), [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: "h3" }), [HEADING_KEYS.h4]: withProps(HeadingElement, { variant: "h4" }), [HEADING_KEYS.h5]: withProps(HeadingElement, { variant: "h5" }), [HEADING_KEYS.h6]: withProps(HeadingElement, { variant: "h6" }), [HighlightPlugin.key]: HighlightLeaf, [HorizontalRulePlugin.key]: HrElement, [ImagePlugin.key]: ImageElement, [ItalicPlugin.key]: withProps(PlateLeaf, { as: "em" }), [KbdPlugin.key]: KbdLeaf, [LinkPlugin.key]: LinkElement, [MediaEmbedPlugin.key]: MediaEmbedElement, [MentionInputPlugin.key]: MentionInputElement, [MentionPlugin.key]: MentionElement, [ParagraphPlugin.key]: ParagraphElement, [SlashInputPlugin.key]: SlashInputElement, [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: "s" }), [SubscriptPlugin.key]: withProps(PlateLeaf, { as: "sub" }), [SuperscriptPlugin.key]: withProps(PlateLeaf, { as: "sup" }), [TableCellHeaderPlugin.key]: TableCellHeaderElement, [TableCellPlugin.key]: TableCellElement, [TablePlugin.key]: TableElement, [TableRowPlugin.key]: TableRowElement, [TocPlugin.key]: TocElement, [TogglePlugin.key]: ToggleElement, [UnderlinePlugin.key]: withProps(PlateLeaf, { as: "u" }), };
You got a solution? I have same problem.
You're omitting the components so this would serialize to default div and span without any styles. The workaround to above errors is to pass your own context-free components this way:
const tmp = createPlateEditor({ plugins: [HtmlReactPlugin], override: { components: { p: HtmlParagraphElement, // ... } } })
I'm doing it like this as well, but I get plain divs with none of the applied styles:
<div>Playground</div><div class="slate-p">A rich-text editor with AI capabilities. Try the AI commands or use Cmd+J to open the AI menu.</div>
const tmpEditor = createPlateEditor({ plugins: [HtmlReactPlugin], override: { components: editorComponents, }, }) const html = tmpEditor.api.htmlReact.serialize({ nodes: editor.children, convertNewLinesToHtmlBr: true, stripWhitespace: false, dndWrapper: (props) => ( <DndProvider backend={HTML5Backend} {...props} /> ), }); export const editorComponents = { // [AIPlugin.key]: AILeaf, [BlockquotePlugin.key]: BlockquoteElement, [BoldPlugin.key]: withProps(PlateLeaf, { as: "strong" }), [CodeBlockPlugin.key]: CodeBlockElement, [CodeLinePlugin.key]: CodeLineElement, [CodePlugin.key]: CodeLeaf, [CodeSyntaxPlugin.key]: CodeSyntaxLeaf, [ColumnItemPlugin.key]: ColumnElement, [ColumnPlugin.key]: ColumnGroupElement, [CommentsPlugin.key]: CommentLeaf, [DatePlugin.key]: DateElement, [EmojiInputPlugin.key]: EmojiInputElement, [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: "h1" }), [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: "h2" }), [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: "h3" }), [HEADING_KEYS.h4]: withProps(HeadingElement, { variant: "h4" }), [HEADING_KEYS.h5]: withProps(HeadingElement, { variant: "h5" }), [HEADING_KEYS.h6]: withProps(HeadingElement, { variant: "h6" }), [HighlightPlugin.key]: HighlightLeaf, [HorizontalRulePlugin.key]: HrElement, [ImagePlugin.key]: ImageElement, [ItalicPlugin.key]: withProps(PlateLeaf, { as: "em" }), [KbdPlugin.key]: KbdLeaf, [LinkPlugin.key]: LinkElement, [MediaEmbedPlugin.key]: MediaEmbedElement, [MentionInputPlugin.key]: MentionInputElement, [MentionPlugin.key]: MentionElement, [ParagraphPlugin.key]: ParagraphElement, [SlashInputPlugin.key]: SlashInputElement, [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: "s" }), [SubscriptPlugin.key]: withProps(PlateLeaf, { as: "sub" }), [SuperscriptPlugin.key]: withProps(PlateLeaf, { as: "sup" }), [TableCellHeaderPlugin.key]: TableCellHeaderElement, [TableCellPlugin.key]: TableCellElement, [TablePlugin.key]: TableElement, [TableRowPlugin.key]: TableRowElement, [TocPlugin.key]: TocElement, [TogglePlugin.key]: ToggleElement, [UnderlinePlugin.key]: withProps(PlateLeaf, { as: "u" }), };
@Divanny
U need to specify every className that your custom components uses.
For example u have smth like this
export const ParagraphElement = withRef<typeof PlateElement>(
({ children, className, ...props }, ref) => {
return (
<PlateElement
ref={ref}
className={cn("m-0 px-0 py-1", className)}
{...props}
>
{children}
</PlateElement>
);
},
);
you need to specify every className in serializer
const html = tmp.api.htmlReact.serialize({
nodes: editor.children,
stripDataAttributes: false,
stripWhitespace: false,
dndWrapper: props => <DndProvider backend={HTML5Backend} {...props} />,
preserveClassNames: [
"relative",
"m-0",
"px-0",
"py-1",
"slate-p",
"slate-selectable",
],
});
hope this helps
Description
When calling
serializeHtml
(from@udecode/plate-serializer-html
), React throws an invalid hook error:Steps to Reproduce
I have a component,
CustomPlateEditor
. In this component, I have:My
editorRef
is passed down from a parent component, which contains the following code:When making any sort of change inside my Plate editor, I end up getting the invalid hook error, which I've narrowed down to only happening due to the
nodes: editorRef.current.children
part of the serializeHtml function. I've tried various other approaches including creating a temporary editor and passing in the editor value, but I get the same React hook error. I've checked that I don't have different versions of React running, so it seems to be an issue within@udecode/plate-serializer-html
Sandbox
I will make a code sandbox within a couple of days and update this issue.
Expected Behavior
I would expect that, with the given code I have in my app, making any change to the Plate rich text editor would result in a raw html string being set for my
editorHtmlValue
state value.Environment
slate@0.101.4
slate-react@0.101.3
@udecode/plate-common@27.0.0
Funding