8

I want to generate HTML format from editorState in Lexical Rich Editor, I'm able to get selection with editorState and what will be best to save into database, HTML or some sort of JSON format?

and I want to show this HTML outside of editor. here is some example of code

const onChange = (editorState) => {
  const editorStateTextString = editorState.read(() => {
    const selection = $getSelection();
    
    console.log(selection);

    return $getRoot().getTextContent();
  });

  // TODO: saving text only at the moment
  if (changeHandler) {
    changeHandler(editorStateTextString);
  }
};

<LexicalComposer initialConfig={editorConfig}>
  <div className="editor-container">
    <ToolbarPlugin aditionalTools={aditionalTools} />
    <div className="editor-inner">
      <RichTextPlugin
        contentEditable={<ContentEditable className="editor-input" />}
        placeholder={<Placeholder placeholder={placeholder} />}
      />
      <OnChangePlugin ignoreInitialChange onChange={onChange} />
    </div>
  </div>
</LexicalComposer>

3 Answers3

11

I have been running into the same issue and solved it by using the following simple plugin. The hard part was getting a reference to the editor, which is what I used useLexicalComposerContext() for.

import { useState, useEffect } from "react";
import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { $generateHtmlFromNodes, $generateNodesFromDOM } from "@lexical/html";
import { $insertNodes } from "lexical";

interface Props {
    initialHtml?: string;
    onHtmlChanged: (html: string) => void;
}

const HtmlPlugin = ({ initialHtml, onHtmlChanged }: Props) => {
    const [editor] = useLexicalComposerContext();

    const [isFirstRender, setIsFirstRender] = useState(true);

    useEffect(() => {
        if (!initialHtml || !isFirstRender) return;

        setIsFirstRender(false);

        editor.update(() => {
            const parser = new DOMParser();
            const dom = parser.parseFromString(initialHtml, "text/html");
            const nodes = $generateNodesFromDOM(editor, dom);
            $insertNodes(nodes);
        });
    }, []);

    return (
        <OnChangePlugin
            onChange={(editorState) => {
                editorState.read(() => {
                    onHtmlChanged($generateHtmlFromNodes(editor));
                });
            }}
        />
    );
};

export default HtmlPlugin;

You can use it in your LexicalComposer like so:

<LexicalComposer initialConfig={editorConfig}>
  <div className="editor-container">
    <ToolbarPlugin aditionalTools={aditionalTools} />
    <div className="editor-inner">
      <RichTextPlugin
        contentEditable={<ContentEditable className="editor-input" />}
        placeholder={<Placeholder placeholder={placeholder} />}
      />
      {/* see here */}
      <HtmlPlugin
        onHtmlChanged={(html) => console.log(html)}
        initialHtml="<h1>Test</h1><p>Lorem ipsum dolor sit amet</p>"
        />
    </div>
  </div>
</LexicalComposer>
Steve2955
  • 639
  • 1
  • 5
  • 17
0

You should definitely save the JSON into the database. The most important thing is that this let's you decide how to render it. Maybe in some cases you want to render to HTML, but in others (eg: mobile) you want to render to native elements.

To get the JSON structure you can do:

editor.getEditorState().toJSON();

Also, regarding your second question. Here's how you can get the HTML:

import {$generateHtmlFromNodes} from '@lexical/html';

...    

const htmlString = $generateHtmlFromNodes(editor, null);

NOTE: you need to call the above method inside the Lexical context (ie: inside a callback like editor.update

0

You can grab editor from initialConfig using the editorState key.

const initialConfig = {
  editorState: (editor) => {
    editor.update(() => {
      const parser = new DOMParser();
      const dom = parser.parseFromString(htmlString, ‘text/html’);
      const nodes = $generateNodesFromDOM(editor, dom);
      $insertNodes(nodes);
    }):
  }
}

Then just pass initialConfig to LexicalComposer.

ZetiX
  • 1
  • 1