Bug: Extremely slow performance in big documents
Lexical version: 0.29.0
Steps To Reproduce
- Create an extremely basic editor. No plugins, no anything, just
const config = {
namespace: 'test',
onError: console.error,
}
editor = createEditor(config)
editor.setRootElement(ref)
- Copy a large piece of text to the clipboard in a browser. I just selected all on a big Hacker News comments thread (about 500 kB characters) and copied it.
- Paste it into lexical using Cmd + V.
- Paste it into lexical using Cmd + Shift + V.
The current behavior
- Very slow pasting with Cmd + V.
- Extremely slow pasting - taking minutes(!!) on a M1 Macbook Pro
- Super slow text editing once the text has been pasted. Almost 1 sec latency for inserting/deleting a character.
This is all in plain text mode. No plugins, no nothing, just a core editor.
The expected behavior
Prosemirror does all of these nearly instantly. Pasting is max 1-2 seconds, latency is fluid even with a big document.
I'm considering migrating to Prosemirror, just because of this performance problem on large documents.
Do you know if this is inherent on the design of lexical, or there is a chance of solving this?
Lexical's architecture works fine until you have a lot of nodes (probably on the order of hundreds of thousands) and then it doesn't work particularly well because of the way the NodeMap is stored and how naive its garbage collection for nodes is (scanning all nodes). There are ways to fix this but it isn't a priority for anyone at the moment. If you do intend to work with really large documents I would recommend that you use something designed for that, or plan to invest in improving lexical one way or another (by writing PRs to improve it or paying someone else to).
Here's a link to a discussion on discord about ways that NodeMap could be improved to suit large documents https://discord.com/channels/953974421008293909/964928369793843230/1303854088596881510
Once that happens it would be straightforward to improve the reconciler's garbage collector to scan fewer nodes.
I haven't done any profiling on it but importDOM could probably be optimized as well.
If it is a rich text editor written by canvas, or a virtual dom implementation, you can achieve a lot of dom rendering, similar to Google's online documents , Or Tencent Documentation
Thank you for the replies. It's not a rich text editor, I simply want to make it behave exactly like a textarea for a start. Plain text only, no plugins, except history.
I guess I can overwrite the paste handler and implement it myself. But then, the normal, editing real-time performance still has a very high latency. I don't know how are tokens counted (is <p> one token?) but ProseMirror React puts the 1.1 million character Moby Dick book on their official demo, and it's perfectly smooth.
The history support that it ships with is not memory efficient when you have documents with a lot of nodes. This is for the same reason that the editor isn’t, history just holds snapshots of the EditorState. Its performance would be improved by the same sort of optimizations that would help the editor in general.
Lexical does not do any rendering virtualization at all. All lexical nodes have at least one DOM element. It does not support alternate renderers such as canvas.
Per your question about tokens, <p> is a ParagraphNode, the <span> inside it is a TextNode, and each <br> is a LineBreakNode so each paragraph is roughly 1 + 2 * lineCount nodes (assuming the lines are non-empty).
One quick-fix hack could be to disable the <HistoryPlugin/> if the editorState exceeds some threshold, tho of course then you'd loose undo/redo, maybe you could then store snapshots in IndexedDB, localStorage or your database.
Another might be to add your own virtualization (using TanStack Virtual for example), tho not sure if Lexical could get confused 🤔
The only rendering you can virtualize in a lexical document is the contents of a decorator node, all other rendering is managed
@hyperknot
Thank you for the replies. It's not a rich text editor, I simply want to make it behave exactly like a textarea for a start. Plain text only, no plugins, except history.
I guess I can overwrite the paste handler and implement it myself. But then, the normal, editing real-time performance still has a very high latency. I don't know how are tokens counted (is
<p>one token?) but ProseMirror React puts the 1.1 million character Moby Dick book on their official demo, and it's perfectly smooth.
I copy-pasted content from https://handlewithcarecollective.github.io/react-prosemirror/ to lexical-playground https://playground.lexical.dev/, it works fast...
We are experience slow performance with 500kb documents. But on initial load. if you'll find the solution, please, let me know.
I think they must have fixed some of the performance issues, now pasting Moby Dick seems to be almost as fast as with ProseMirror.