I think the biggest mistake people make about Lexical, is assuming it’s not going to be as good as X, because X does it perfect already. Like, that might be true, but is also a general flaw in the frontend software circles. I was on the React Core team for 3+ years, and we spent a lot of time looking into APIs and how we could eliminate whole classes of issue by simply making the edge-cases implementation details of the framework itself. The same thing applies to Lexical.
We look at Lexical as being the React of editors – in terms of its declarative approach to promoting imperative APIs and how you go about reasoning intent. An example might be schemas from ProseMirror. Lexical doesn’t have schemas as a hot keyword, but it does offer the same functionality via Lexical’s node APIs. Many ElementNode and TextNode methods define the intent of an interaction or a relationship with a parent, sibling, or child. For example, see: lexical/LexicalElementNode.js at main · facebook/lexical · GitHub. You can extend one of the Lexical core nodes and just over-ride those methods and specific your heuristics for your node as needed. Which is far less boilerplate, and fully type-able in Flow/TypeScript – which is priority at Meta.
The reason we have to build these things, is because we need to have them at scale for Facebook, WhatsApp, Messenger, Instagram, Oculus etc. That means, we’re also investing in building native versions of Lexical for mobile (in native mobile languages) and also investing into building React Native bindings for those who use those surfaces. So we need something that can scale now, not something that can scale later. Lexical is already used by billions of people everyday, and we plan on expanding that to 3+ billion in the future. So Lexical most definitely isn’t a toy.
Lexical is in a fortunate position, where we can take from what has existed previously – including ProseMirror, React, Yjs, and leverage it with latest browser APIs. Lexical doesn’t support IE, or legacy Edge, and we work closely with engineers from Mozilla and Google to patch bugs in their evergreen browsers to ensure all web editors work better (that’s why we leverage
beforeinput so much).
We are also trying to do best in-class for accessibility, given we have some of the best accessibility developers and advocates working at Meta with us. So we’ve taken time to make sure Lexical works properly for users of all capabilities. VoiceOver, Dragon Naturally Speaking and JAWS being key tools that fail with most web editors today. Plus, we’re aware of existing issues and we’re transparently tackling them. A good example is the horizontal caret that is awesome in ProseMirror. Unfortunately, this doesn’t pass WCAG guidelines and doesn’t work well on mobile, so whilst we supported this in the past, we’re now going back to look at how we can insert whitespace upon moving selection to a place where the user might struggle with it (like a paragraph, for example).
For collaboration, I worked closely with Kevin (author of Yjs) to ensure we could “sync” the Lexical editor state between many clients. We use the tree structure of Lexical to sync to Y.XmlElement, and Y.Text accordingly. We flatten text within a given block, and use inline Y.Map nodes between characters to infer the beginning and end of Lexical TextNodes. The Y.Map also holds all the properties of the TextNode, or user extended TextNode and scales well without having to use attribute ranges, which isn’t performant in Lexical’s case with CRDT. We are internally shipping Lexical with Yjs today and we hope to expand this in the future to more surfaces, including mobile.
If anything, the reason I’m here on the ProseMirror discussion board is to praise ProseMirror’s efforts and ideas. Without them, we wouldn’t have the Lexical we have today. We just wanted to take our spin on how we could do it, and given my experience building React and Inferno, I thought my twist on it might be appealing to this community. Innovation doesn’t end, it continues and that’s what open source is all about.