<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.
ProseMirror 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.
is there a list/writeup of these problems somewhere? is this only applicable if you want to do editable rich text? could you perhaps try to explain the high level problem? what's different on iOS? thanks!
Writing a ContentEditable editor means learning the internal state machines and undocumented behaviors of every browser you support. There are many articles online about ContentEditable / rich text editing. This one is a classic https://medium.engineering/why-contenteditable-is-terrible-1...
In English, usually it’s okay to assume one key press event = one letter input. The opposite is true on Android generally, and for CJK languages on all platforms. These use input systems (like handwriting) where keypresses never occur, or many keypresses compose to produce one character. There are also new rules for IME composition input like “you can’t touch the DOM or change the selection during composition or the input will cancel and your user will be mad”. It turns the problem from “Hard” difficulty to “Nightmare” difficulty.
The baffling thing on Android is that each keyboard (Gboard, Samsung Keyboard, Swiftkey, Simeji, Sogou, Naver) have different composition behaviors with different event patterns… and GBoard’s ENGLISH input is probably the most strange! It composes whole English words like they were Chinese characters, and it wants to compose SO badly it will move text selection from wherever the user tapped to the nearest English word. And as an added bonus on Android only, the browsers selection APIs report the wrong selection during different phases of the event flow. Hah. Like when you tap a key, Android will send a selectionChange event before the first input-related event, and that selectionChange will probably report some weird lie about the selection position.
wow. at this point to me it seems the next step is to implement the keyboard in JS too, and/or organize a protest in Mountain View.
also, opting to use a big old plaintext textbox and something markdown-ish avoids these problems, right? (I'm trying to figure out why typing becomes slow on discord/slack in Firefox routinely. though I guess those are not real textboxes, but also contenteditable things :/)
I work at Notion and recently rebuilt much of the editor core, and I don’t think Slate is a good choice because it considers nice, simple code more important than CJK or Android support. Notion doesn’t use Slate or any other editor framework. I just study a lot of editor core code.
1. Android also has a browser. It is an important platform with many users.
2. Building an editor is so complicated that most organizations want to do it only once. They build a web editor, and wrap it in a native app. The shell like file browser, sharing screens, etc are “native” but the editor surface is a webview. This is how Quip, Dropbox Paper, Google Docs, Notion, etc work. Even iOS worked this way initially for all editable styled text (per https://twitter.com/kocienda/status/1400484473540513792?s=21...)
3. If you are going to bet the core competency of your business on a framework, it’s important to understand the motivations and limitations of said framework.
Slate has a nice, approachable API from a React perspective. I think depending on your project it could be an okay trade off.
I co-wrote a post about our editor internals after finishing up the recent re-work but we decided not to publish it to conserve our Competitive Advantage.
Since it doesn’t implement editing itself I haven’t studied its source. Under the hood it’s ProseMirror so it should be okay, although I’d be uneasy about stacking abstractions too high: Your code —> TipTap React —> TipTap Extension —> ProseMirror Plugin —> ProseMirror Core —> DOM feels like there’s a lot of incidental complexity to learn all of the APIs across these layers. Right now the docs site is returning HTTP 502 to me ¯\_(ツ)_/¯.
The 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
I've given Slate and Draft.JS a go in the past and they both aren't great from a developer experience perspective and are generally buggy. Slate also had issues where it would throw a type error and break the page, Draft.JS struggles with cursor placement issues.
Example: https://www.slatejs.org/examples/richtext
Code: https://github.com/ianstormtaylor/slate/blob/main/site/examp...