Comparing different ProseMirror react implementations

Why we want to use ProseMirror with React

We are a very react-heavy shop and thus searched for ways to utilize react for richer visualizations within the ProseMirror editor. Besides our toolkit in React, we also have existing React components that we want to reuse within the editor

Options for React within ProseMirror

We found 2 different high-level approaches to combine those 2

  1. ReactApp Implement ProseMirror as a ReactApp, thus spawning the entire editor using react including the PM-state being a first-class citizen react state. The latter might be optional
  2. NodeView Use ProseMirror NodeViews with React-Portals to use react components as visualizations of prose-mirror nodes
    • There are only people talking about this and there, it has not manifested in a toolkit or project

Comparing the options

We tried to compare the options and understand, how they differ, what pitfalls each option might have, and in the end, what option is the best for us (or in general)

ReactApp: The react-app variant seems like the cleanest approach, pulling everything into React, and making the PM a toolkit/SDK within PM. While this is what is done in react, it is problematic with PM in particular. PM has its own rendering engine/idea due to its nature in providing efficient updates of the editor but also in gathering changes from the view back to the state. This rendering interferes with reacts own rendering, resulting in different issues excellent write-up by handlewith-care-collective

NodeView: This variant uses ProseMirror NodeViews, which have been built for complex visualizations of editor-widget/nodes, and renders a react-app for each node (usually within a portal). The PM-state is not known to the React-Apps, while the apps will still use PM transitions to manipulate/interact with the PM-state. First, we thought this option dodges most of the issues we found in the ReactApp option, but in the end, it turned out to have the same last 2 paragraphs here by handlewith-care-collective. For use, we name them the issue with the Power-Struggle and the issue with the Future-State.

Bottom line, while we thought NodeView might be our safe haven, it is not.

Moving forward/Questions

Especially after reading the readme of react-prosemirror, which is an excellent overview of the problems different people encountered, including the iterations and options to combat those, we understood, that we cannot dodge the bullet with using NodeView for react. So in the end one option is left to be chosen from, that is the ReactApp approach. The most promising ‘library’/not opinionated variant of all the mentioned above seems to be react-prosemirror to us.

Considering the effort put into the current iteration of react-prosemirror, replacing PM’s EditorView with a react implementation, it is hardly realistic to see the react integration as an issue every shop has to tackle individually. Also, @marijn stated in Official Roadmap or future plans - #2 by marijn, that PM will not lean into integrating with react or making things easier in general - meaning all the heavy lifting will be done by third-party projects like react-prosemirror.

So sticking heads together seems like a natural strategy to get something substantial. React-prosemirror seems to be open to contributions and seems to care about people being aware of the fundamentals of the issue and they already have a solution in place.

Talking about the latter, I am not sure how robust the current iteration is to be judged. So why not start discussing this, happily inviting @SMores to the thread. What are your thoughts and especially, does the comparison above make sense / please correct it. But please anybody join in, share your opinions.

Hi @EugenMayer! This seems like a pretty good representation of the landscape. I would consider React ProseMirror ready for production use (indeed, there are multiple companies using it in production!) and stable. The API is unlikely to change, and I feel confident in the implementation as well. I’ve ported over the entire ProseMirror View test suite to the library and all changes have to be green to be merged. The library has an explicit goal of being low-level and supporting integration with all existing ProseMirror patterns and libraries (or at least as many as possible).

One important exception is that the library does not work on Chrome on Android. I have plans to resolve this, but at the moment, we rely on a beforeinput-based editing implementation that’s incompatible with the composition-driven editing that Chrome uses on Android. Composition works, generally, so you can type in a non-Android browser with a non-English keyboard.

It actually seems that this isn’t totally true anymore! Android Chrome doesn’t seem to be working totally as expected, but it does largely work. Notably, the editor demo (which has the entire contents of Moby Dick) crashes the browser on Android Chrome (but not Opera), but smaller documents work essentially fine (currently the “Enter” key handler doesn’t work for some reason but that seems very fixable).

1 Like

Thank you a lot for your insight about the state of react-prosemirror - that sounds promising already! We are currently still in the ‘which editor to pick finally’ last phase, with lexical being the last competition. But for sure we would update or PM-PoC using react-prosemirror next.

One thing I have open is @marijn statements in Official Roadmap or future plans - #2 by marijn

attached to it. react-prosemirror might be worth looking at if you want to put React components inside of the editor, but comes with its own complications, due to the way React introduced hard-to-control synchronicity.

@marijn could you comment on what you mean by " React introduced hard-to-control synchronicity"? Are you talking about something recent that has been added or a general concept?

I mean that just about everything has to be wrapped in hooks to synchronize your code to the React update cycle.

FYI, you can use both approaches in ProseKit implements. Here is an example that (a) renders the editor as an <Editor/> React component, and (b) renders a particular node codeBlock as a React component (in code-block-view.tsx).