20 ms·
Lexical – a web text editor framework that powers Facebook
- trueadm 4y agoI'm the author of Lexical and one of the many engineers working on Lexical full-time at Meta. If you'd like to know anything, or ask any questions, please do! For those of you looking for the sourcecode for the playground, you can find it here: https://github.com/facebook/lexical/tree/main/packages/lexical-playground https://github.com/facebook/lexical/tree/main/packages/lexic... We also have a Discord channel you can check out: https://discord.gg/KmG4wQnnD9 https://discord.gg/KmG4wQnnD9
- WhitneyLand 4y agoA really nice project. A recruiter called me from your company recently, and I kind of cringed hearing him talk about the meta verse. I realize now they probably would be much more effective with engineers if they skipped that part and just focused on all the cool work that’s being done.
- nehalem 4y agoThanks for the offer, let me take you up on this. Could you compare Lexical and Prosemirror from an architecture perspective? What motivated your decisions in that regard?
- trueadm 4y agoSure thing. I actually wrote up a really detailed response to this on ProseMirror's discussion board: https://discuss.prosemirror.net/t/differences-between-prosemirror-and-lexical/4557 https://discuss.prosemirror.net/t/differences-between-prosem... Ultimately though, there are many similarities between the two – intentionally so. We were inspired by ProseMirror and some of its APIs and approaches. I think the biggest underlying differences are in how we tackle things from a DX perspective. We tried to bring a more composable API to making text editors work – from the plugin patterns, how updates and reads work, to how you listen for changes and react to them. Much of this was inspired by my prior work working on the React core team and from creating Inferno.
- samwillis 4y agoInteresting you say DX is a large aspect of what you are going for. ProseMirror is awesome but the API can be somewhat complex for simple things (but at the same time incredibly powerful when you get to understand it). I absolutely love TipTap[0] which is providing a much more user friendly API on top of ProseMirror, along with a clean Plugin system. Going to have to have a play with Lexical, excited to see Yjs support! [0]: http://tiptap.dev http://tiptap.dev
- chucky123 4y agoFundamental problem with Tiptap is its horrendous docs. If lexical can put out better docs, it is better than Tiptap automatically.
- singaporecode 4y agoProseMirror has some of the worst developer experience possible. I wasted so much time figuring out it’s convoluted API and abandoned it soon after. It does apparently have the best live editing / collaboration features of all text editors out there.
- samwillis 4y agoI would disagree on "worst developer experience possible", it has a very steep leaning curve, but once you grok it it makes a lot of sense.
- pixel_tracing 4y agoHey since you work at Meta can you find when code support / markdown will be added to text editing components? It’s so frustrating posting code snippets to friends threads on FB
- trueadm 4y agoLexical supports code support / markdown. We actually use it a lot for Workplace, which is a kind of Facebook-like website for companies. It's really down to the product surfaces what features from Lexical they choose to use.
- jacobsimon 4y agoHey on my business’s website we currently use draft.js for our user discussion product and we convert the user input to markdown for storing in our db. How hard would it be to switch to Lexical and does it support rendering from markdown syntax? Do you recommend any other approaches for safe storage and rendering of rich text? Thanks!
- trueadm 4y agoLexical has markdown support via `@lexical/markdown`. See https://lexical.dev/docs/api/lexical-markdown https://lexical.dev/docs/api/lexical-markdown.
- acywatson 4y agoI work at Meta on the Lexical team - yes, Lexical supports markdown and rendering from markdown syntax. Lexical isn't a drop-in replacement for Draft, but we're migrating all of our surfaces internally and it isn't particularly difficult, especially if you aren't storing in the DraftJS-specific format. As for alternatives, you can also consider storing it as JSON, which Lexical supports.
- jjuliano 4y agoHi, I did (or somewhat invented/devised) a similar concept, but for visually programming UI design. See: https://jjuliano.github.io/markdown-ui/docs/container.html https://jjuliano.github.io/markdown-ui/docs/container.html
- jjuliano 4y agosorry, I didn't realize this was a text editor. I was initially looking at the time-travel screen (the black box), and thought that is where to type things to be able to use it. Lexical is awesome!
- hit8run 4y ago
- hemantv 4y agoSeriously wtf
- hemantv 4y agoAwesome work. I have been early user of Lexical building getstable.co. Thanks for great framework and design ;)
- guessmyname 4y agoWhat does “web text editor framework that powers Facebook” actually mean? I don’t use Facebook, but AFAIK, there are no “blog features” in the platform, are there? Other than a WYSIWYG editor [1], what other use cases do you intend this project for? [1] https://en.wikipedia.org/wiki/WYSIWYG https://en.wikipedia.org/wiki/WYSIWYG
- chii 4y agosee https://news.ycombinator.com/item?id=14838232 https://news.ycombinator.com/item?id=14838232 > The competition has no choice but to spend all their time porting and keeping up, time that they can’t spend writing new features. Look closely at the software landscape. The companies that do well are the ones who rely least on big companies and don’t have to spend all their cycles catching up and reimplementing and fixing bugs that crop up [only on Windows XP.] so it is the same with the modern day web frameworks as it is in the old days of windows and microsoft's dominance. Joel Spolsky was as right in early 2002 as he is right in 2022.
- code_biologist 4y agoSee also “nerd sniping”: https://xkcd.com/356/ https://xkcd.com/356/
- groby_b 4y agoJoel Spolsky wasn't particularly right in 2002, and he hasn't gotten more relevant with age. As for this particular article, what is your criticism? That FB makes a rich text editor available as open source (under the MIT license)? How on earth does that mean "companies have to spend all their cycles catching up and reimplementing and fixing bugs"? You can always roll your own, or use one of the many others available if this one doesn't taste right? (Also: Who on earth gains a competitive advantage from having a text editor, in 2022?)
- chii 4y ago> How on earth does that mean "companies have to spend all their cycles catching up and reimplementing and fixing bugs"? The fact that there's draft.js, and a myriad of other existing editor libraries out there already, all of which could've been contributed to instead. If a startup chose to base their technology on a library that is released by a major corporation, they face the risk of getting "rug pulled" (or the library updates incompatibly and now you're locked in either to using the old version, or painful upgrade to the new version). Of course, they don't have to choose to use such a library, but if they don't then there's a community of people who then questions them on why they are spending time replicating a technology. The point is, if a startup chose to use this library, they face the risk of having to expend time keeping up in pace (and associated cost of doing so) with facebook. This equivalent scenario exists in someone writing against libraries released by microsoft on windows, and that's what spolsky is complaining about.
- tumultco 4y agoThanks for your work in developing this! Does Lexical have a feature to set arbitrary CSS on a particular selection range? I've been looking for a solution to do this since the WebKit's -[WebView applyStyle:] is deprecated as part of WebView Legacy. It isn't clear to me from glancing at the docs if RangeSelection.formatText() or other methods can handle anything or just a subset of styling code. Thanks!
- afd8856 4y agoJust mentioning SlateJS which has [decorations](https://docs.slatejs.org/concepts/09-rendering#decorations https://docs.slatejs.org/concepts/09-rendering#decorations) for this
- acywatson 4y agoYes, we have something like this in @lexical/selection: https://github.com/facebook/lexical/blob/af099ffd9f464b523d66ae4e507e170e62e49860/packages/lexical-selection/README.md#patchstyletext https://github.com/facebook/lexical/blob/af099ffd9f464b523d6...
- chx 4y agoDoes wordperfect 5.1 say anything to you :) ? You just remade it and I love you for that.
- taf2 4y agoDoes it have vim keybindings? And does it need react or is there a standalone way to embed it?
- zwily 4y agoPeople have contributed bindings for Vue, Svelte, and Solid so far. I’m not sure what state they are in though. React is definitely the most mature binding.
- kapravel 4y ago> Prevent the DOM from being manipulated externally (i.e. extensions) to guarantee that the DOM always matches the EditorState. [1] Can you talk a bit more about how this is achieved? Is it DOM obfuscation? [1] https://discuss.prosemirror.net/t/differences-between-prosemirror-and-lexical/4557 https://discuss.prosemirror.net/t/differences-between-prosem...
- bythckr 4y agoWhy not add a spell checker & word counter?
- jitl 4y agoHow do you handle Android and multi-lingual input? Writing the state machine to deal with Chrome/Android’s bananas selection bugs in Notion’s editor gave me a lot of headaches. Do you add extra DOM nodes on Android? Any key insights that help deal with IME on Android?
- trueadm 4y agoAndroid is indeed a headache to support. The biggest issue is how all keyboards force composition on all keystrokes, meaning you're constantly having to read the DOM input and work out what might have been entered. We don't use extra DOM nodes, but we do listen to `beforeinput`, `input` and use DOM MutationObserver to try and detect common Android patterns. I'd search this module for references to Android: https://github.com/facebook/lexical/blob/main/packages/lexical/src/LexicalEvents.ts https://github.com/facebook/lexical/blob/main/packages/lexic...
- jitl 4y ago> event.timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY I tried so hard to avoid adding timeouts/timestamp logic for Android because it feels like a nondeterministic hack. In the end I gave up and did something similar. I also found beforeInput doesn’t offer much solace on Android or with CJK language, because messing around with preventDefault or the DOM during composition disturbs the user. One weird thing I never figured out is that if the user puts the selection in an empty block, GBoard wants to jump to the nearest word in a different paragraph. We do some brutal hacks to get around that :-/ I read your comment on ProseMirror forum about preventing interference from Granmarly, etc. Do you do this by reverting MutationRecords? Where is the code/docs for that?
- trueadm 4y agoYeah, I really wanted to avoid them too. There doesn't seem a good way. The issue you describe with selection jumping is a well known issue with GBoard. Here's the logic for the mutation handling: https://github.com/facebook/lexical/blob/main/packages/lexical/src/LexicalMutations.ts https://github.com/facebook/lexical/blob/main/packages/lexic...
- rsstack 4y agoI'd love to use Lexical to replace what we're using today. I've been checking in on the repository every few weeks. However, there are many features that are still pending https://github.com/facebook/lexical/projects/1 https://github.com/facebook/lexical/projects/1 . We need several of the features that are "V1.0" and we obviously need the stability of a 1.0 SemVer release :) Is there a general timeline goal for the V1.0 release?
- trueadm 4y agoWhat features in particular are you missing? We haven't really discussed a deadline for 1.0, as we've been trying to take our time making sure each release is impactful and correct.
- 8organicbits 4y agoHow is Facebook using this?
- trueadm 4y agoIt's used throughout Meta. The Post Composer, and Comment Composer on Facebook/Workplace, on Messenger, WhatsApp Web, Instagram Messenger, and many other surfaces too!
- tester756 4y agoThat's really good How do people search for jobs that work in this problem domains - parsers / editors / compilers?
- trueadm 4y agoFor Meta, or in general? For Meta, there are plenty of teams that require some infra knowledge around these common concepts. It's been fairly straightforward to find a team as a front-end engineer that allows you to excel in these areas at Meta (once you're successful in the interview loops).
- a-priori 4y agoHow does this compare to Draft.js, another rich text editor created by Facebook? https://draftjs.org/ https://draftjs.org/
- trueadm 4y agoCheck out this answer I used in a previous HN thread: https://news.ycombinator.com/item?id=31020205 https://news.ycombinator.com/item?id=31020205
- pxeger1 4y agoIn what sense does this "power" Facebook? Do you mean it's used for most or some specific text editing components of Facebook?
- trueadm 4y agoIt's replacing any previous web text editor UX components that we had in the past (likely DraftJS). We're also building Lexical for iOS which is slowly rolling out across iOS devices too.
- andrewshadura 4y agoWell, I want the previous editor back. In fact, just give me a text area. The current editor breaks the selection and, in turn, the clipboard manager, it doesn't work well with Compose and dead keys, and it sometimes ties itself into a knot, and the text can't be edited anymore.
- danuker 4y agoWhen pasting links stopped working for me is about when I stopped using Facebook. It's 2022 and we're reinventing every wheel, but in the browser. GitHub has a Markdown editor with a paste handler (converting tables and such). It once had a bug. I had to disable handlers in the browser to be able to paste.
- dbbk 4y agoA textarea would not work
- jamespetercook 4y agoVery interested to know if there are any plans to support React Native? I’ve been struggling to find a good library that doesn’t require a web view.
- acywatson 4y agoIt's under consideration, but as of now we need native support for iOS and Android first. React Native doesn't currently support contentEditable.
- barbinbrad 4y agoWe're trying to choose between Lexical and Slate at work. Do you have any code examples that would be similar to this? Example: https://www.slatejs.org/examples/richtext https://www.slatejs.org/examples/richtext Code: https://github.com/ianstormtaylor/slate/blob/main/site/examples/richtext.tsx https://github.com/ianstormtaylor/slate/blob/main/site/examp...
- trueadm 4y agoThe Lexical playground is one giant kitchen-sink example all written in React. You could check out the code for it locally and `npm run start` and play around with removing/adding plugins. You can also check out some examples on Codesandbox: https://codesandbox.io/s/lexical-rich-text-example-5tncvy https://codesandbox.io/s/lexical-rich-text-example-5tncvy
- barbinbrad 4y agoThanks! Very helpful.
- jitl 4y ago<sarcasm>If you are planning to build a Notion competitor, I recommend Slate</sarcasm>. Seriously though, if you need to support Android or CJK, you should understand Slate’s limitations before you buy into it. I think ProseMirror is the best choice today because it has been around for a long time and has battle-tested MutationObserver logic. Lexical is worth considering depending on your risk appetite. It also uses MutationObserver and appears to approach Android correctly. I haven’t read enough of the code to recommend it yet. In Slate, Android is a second-class citizen with an extremely bare-bones MutationObserver reconciler. The main codebase only uses beforeInput.preventDefault which doesn’t work on Android.
- trueadm 4y agoProseMirror is a great choice. Lexical might offer you more mileage if you're working with React (especially React 18 and the new concurrency features) and are happy to invest into a project that is still pre 1.0.
- latchkey 4y agoIt is interesting (and cool) that this uses Typescript over Flow. It seems like Flow is still being developed too. Just looking at the top level package.json [1], I wonder at which point you abstract all those copy/pasted targets into a script that takes an argument. [1] https://github.com/facebook/lexical/blob/main/package.json#L33 https://github.com/facebook/lexical/blob/main/package.json#L...
- _the_inflator 4y agoEven FB engineers can learn a thing or two. ;) Good tip.
- latchkey 4y agoThanks! I guess the last 20 years of writing code were not a waste after all. Heh. =)
- zwily 4y agoWhen they first launched a few months ago it was all Flow, and then it was quickly converted to Typescript. I think they still ship the Flow types too.
- thegreatercurve 4y agoI've been migrating the Lexical codebase to TypeScript recently, especially since it was open-sourced. This was done for a few reasons, but if nothing else, we know TypeScript has a much wider adoption that Flow. Removing learning curves helps to encourage contributions from the community.
- canadiantim 4y agoDoes Lexical require react?
- thegreatercurve 4y agoNo, it can be used with vanilla JS. There is an official (and mature) binding with React, but there are also unofficial bindings with Solid, Vue and Svelte. Each has a dedicated channel in the Discord (including repo links).
- thorncorona 4y agoI realize this isn't probably your product surface but do you have any idea why FB messenger still has issues with double texting using the text box? Also, how do you guys design to reduce latency in Lexical? Esp considering the fluidity of modern chat clients ex. Telegram which are much more performant.
- acywatson 4y agoYou mean typing latency, right? The most important thing we do there is handling reconciliation ourselves rather than delegating to a framework (e.g., React). If you have full control over the reconciliation process, it's just a matter of continuous incremental optimization.
- ketzo 4y agoCould you explain this comment a little more? What does "handling reconciliation rather than delegating" mean here?
- acywatson 4y agoSure - DraftJS, for instance, delegates DOM reconciliation to React. Lexical does not. The core library is framework-agnostic, with it's own diffing and reconciliation processes.
- firecall 4y agoWow, that looks really nice! I'm more excited about this than you'd expect LOL
- chaoxu 4y agoRecently I have been testing vditor, vditor is a markdown editor that have a instant rendering feature [1]. If I'm creating my own editor, how hard is it to implement it using Lexical? [1] https://github.com/Vanessa219/vditor/blob/master/README_en_US.md#instant-rendering https://github.com/Vanessa219/vditor/blob/master/README_en_U...
- laqq3 4y agoThis looks great! Curious whether the team is looking at improving the support of having images in tables cell? I played with the playground and it seems to work, but there seems to be some (relatively minor) glitches/bugs (e.g. difficult to place an image in a cell, moving images across cells seems difficult, and cannot Ctrl-x an image and Ctrl-v to paste it).
- acywatson 4y agoWe definitely have plans to improve Tables drastically over time, but I'm not sure when we'll get to those particular issues. If you care to make an Issue for this on GitHub, that would be much appreciated :)
- mhdhn 4y agoThere do not seem to be text direction controls. I see it does basic heuristic text direction guessing, apparently based on first strong LTR or RTL character in a paragraph, but this is often not adequate for determining text direction correctly, hence the need for explicit control, at least at the paragraph level, ideally also at subparagraph level. Any plans to add such controls? There are about a billion users of bidirectional languages (Arabic, Hebrew, Farsi, Urdu, Yiddish, et al).
- trueadm 4y agoYou can set the direction of the ElementNode explicitly to define LTR/RTL semantics. By default it’s automatically detected.
- mhdhn 4y agoThank you. I see. I guess I'm talking about the playground whereas I guess you're talking about Lexical itself and its API. It would be really nice to have a demo of this working with the API in the playground.
- saimiam 4y agoA few days ago, I was looking to create an editable interface for an email composer for a project I’m working on (https://PretzelBox.cc https://PretzelBox.cc, since you asked). I wanted to see if Lexical was a good fit. My app is plain javascript with a bit of Alpine and htmx. I just couldn’t figure out how to <link> to the appropriate js file in my html file. And I’m not a frontend dev so I have no idea how to browserify (or whatever the technical term is) the right npm modules. I ended up using MediumEditor which is ancient in relative terms. Anyway, now composing email feels like writing a Medium post, fwiw.
- euroderf 4y agoIs it a LOT to ask? => Could the docs PLEASE link to a downloadable runnable self-contained HTML page containing one or two editor instances in Vanilla JS? Same ask for ProseMirror. Maybe such a page would pull the JS from a CDN, maybe it would refer to filepaths on the server. I don't know how to form a working web page from the snippets in the docu because I do not know the relevant javascript. I would just like a working example, on my own computer, with a minimum number of moving parts, that I can play around with.
- lawrencechen 4y agoI put Lexical through Hog Bay's Moby Dick test [0] (paste the entirety of Moby Dick, scroll to the middle, and try editing). It didn't pass. AFAIK, only ProseMirror passes, albeit with some lag. [0] https://www.hogbaysoftware.com/posts/moby-dick-workout/ https://www.hogbaysoftware.com/posts/moby-dick-workout/
- trueadm 4y agoWe have some important changes landing soon that will make insertions O(1) rather than the current O(n) - so this will greatly improve performance.
- nsonha 4y agoClickbait title, what does that even mean for a text editor to "power" facebook
- chrismorgan 4y agoHmm, just tried the playground in Firefox on Linux, it’s about as buggy as fancy things like this tend to be. • Inserting emoji with my Compose key: some (e.g. U+1F641) get turned into image-backed emoji and then duplicated unless preceded by another image-backed emoji; some (e.g. U+1F928) get ignored and left as normal text. • If I type an emoji that gets duplicated while inside a link (but not inside bold/italic), one goes where the caret is, and the other goes immediately before the link, and it leaves the caret there. • The image-backed emoji are incompatible with inline text formatting: if you type them in the middle of formatted text, instead of ending up with the likes of <u>text EMOJI text</u>, you end up with <u>text </u>EMOJI<u> text</u>, and although you can subsequently apply the formatting so that it shows in the debug tree, it doesn’t change the HTML markup (e.g. subscript/superscript/underline/code should certainly be visible, and even italic and bold can change emoji rendering). • If I select part of a link starting outside the end of a link (that is, go one character past the link and then press the Left arrow key—Firefox has valid caret positions for both inside and outside the start and end of an element, depending on which direction you came from, which my faint recollection was a major trouble point on WebKit many years ago, that you couldn’t do that and so literally couldn’t model various reasonable, kinda like multiple selection which is also Firefox-only and extremely useful on tables) and delete that, instead of just deleting the selected characters, it deletes the entire link. (As above, this seems to be specific to links, not affecting bold/italic.) • Very early on (before I had done any editing at all) when I tried the time travel feature, I somehow activated the autocomplete popup on the words “The playground” by clicking or something, even though autocomplete was not turned on (I only confirmed that that was what it was, and not a spelling corrector, by later enabling it and typing “the”) and it was supposed to be read-only. • Resizing table rows and columns is very buggy, with things like the grippies easily getting lost, the table-cell-action-button not updating its position, and it sometimes failing to act on mouse up. (Props for including the functionality, though, as it’s normally overlooked.) • Clicking in table cells always moves the caret to the start of the cell rather than where you clicked, meaning you have to use arrow keys to get to the right place, and can’t select properly by mouse either. • Pop-up menus don’t scroll with the document while open. • Autocomplete popup doesn’t close when you move the caret by mouse, though they will no longer work if you click on them. • Incidentally, who thought it a good idea to apply `list-style-position: inside`? And this was from a fairly quick test. I found most of this stuff immediately and would expect to hit most of these issues in real life. I honestly wish people would try to be less fancy with WYSIWYG components, because the web platform just isn’t well-adapted to it.
- elvin_d 4y agoI tried most of the existing editors and worked with some of them a lot. Lexical have a nice declarative API; so far, enjoying using it. Even without proper docs, I quickly got an idea of what it is and how to develop. React ecosystem lacked a proper text editor and lexical is really promising.