1

When using LexicalComposer, the initialConfig accepts an editorState parameter that can be used to pass in some initial content. If this initialContent is a complex html string, I am not clear how to parse it. For a simple string, I have it like below (everything works fine here):

const initialState = () => {
  const paragraph = $createParagraphNode();
  const text = $createTextNode(initialContent);
  paragraph.append(text);
  const root = $getRoot().append(paragraph);
  root.selectEnd()}

const initialConfig = {
   namespace: 'HTMLEditor',
   theme: editorTheme,
   onError: (error: Error) => {
    throw error;
   },
  editorState: initialState}

Which I then pass in to LexicalComposer. If the string I pass in is NOT a simple string, but rather some html content in the form of a string, and if I do NOT know ahead of time exactly what will be passed in (i.e. I want to parse existing html content created elsewhere) how do I do it? I can't use $generateNodesFromDOM because my editor hasn't yet been initialized.

tolkienfan2
  • 101
  • 1
  • 13

1 Answers1

2

So to get this to work was a multi-step process. First I had to create a plugin to load my initialContent, like this:

type Props = { initialContent?: string }   

export const LoadInitialContent = ({ initialContent }: Props) => {
  const [editor] = useLexicalComposerContext();
  
  React.useEffect(() => {
    if (!initialContent) { return; }
    editor.update(() => {
      const parser = new DOMParser();
      const dom = parser.parseFromString(initialContent, "text/html");
      const nodes = $generateNodesFromDOM(editor, dom);
      console.log(dom, nodes);
      const paragraphNode = $createParagraphNode();
      nodes.forEach(n => paragraphNode.append(n));
      $getRoot().append(paragraphNode);
    });
  }, []);
  return null;
};

Next I had to load this within my LexicalComposer, like this:

<LexicalComposer initialConfig={initialConfig}>
  <LoadInitialContent initialContent={initialContent} />
</LexicalComposer>

Next, in order to properly recognize the various node types, I had to import the types and list them within the initialConfig for LexicalComposer like this:

...
import { ListNode, ListItemNode } from "@lexical/list";
import { HeadingNode } from "@lexical/rich-text";
...
const initialConfig = {
  namespace: 'Editor',
  onError: (error: Error) => {
    throw error;
  },
  nodes: [ListNode, ListItemNode, HeadingNode]
};

And finally I had to import the relevant Lexical Plugins and use them, like so:

...
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
...
<LexicalComposer initialConfig={initialConfig}>
  <LoadInitialContent initialContent={initialContent} />
  <ListPlugin />
  <RichTextPlugin contentEditable={ContentEditable} />
</LexicalComposer>

I'm pretty sure I'll have to do the same to be able to "see" Links and other kinds of nodes too.

supermodo
  • 675
  • 6
  • 15
tolkienfan2
  • 101
  • 1
  • 13
  • The text is being appended twice. Any idea how to avoid that? – Divyanth Jayaraj Apr 27 '23 at 05:28
  • Hard to comment without seeing your code. If you followed the example I posted above the text, should only be appended single time. Perhaps you can post your code here and we might be able to see something that you may have missed – tolkienfan2 Apr 28 '23 at 15:25
  • @DivyanthJayaraj That's happening because you're in a development environment, and the useEffect is firing twice. Try doing this right after `editor.update` and before `const parser = new DOMParser();` `$getRoot().getChildren().forEach((n) => n.remove());` – Gabriel Graves Jun 22 '23 at 05:00