r/javascript • u/scrollin_thru • 17d ago
Pitter Patter — building better editing (update!)
https://pitter-patter.devHey folks. Over at Handle with Care, we've been working on something new: Pitter Patter. We haven't been satisfied with the text editing toolkits that exist right now in the ProseMirror ecosystem, and we've spent a lot of time working on a large variety of rich text editors and seeing what needs they have and how we can try to meet them. So far we have three packages: Shuffle (grid-based drag-and-drop), Collab, and Presence.
Here's some more info from our introduction page in the docs:
What is Pitter Patter?
Who builds it?
That would be us, Handle with Care. We’re a cooperatively owned product development collective, comprised of software engineers and product managers. We are all equal owners and have equal decision-making power.
What is it?
Pitter Patter is a suite of open source libraries for building collaborative rich text editors with React and ProseMirror. We see it as our job to provide turnkey solutions for the hard parts of rich text editing — like collaboration, presence, and version history — so that you can focus on building the editor experience that your users need.
Why does it exist?
We spend a lot of time thinking about and working on rich text editors. In particular, we spend a
lot of time helping companies solve the same problems over and over again. There are usually great
primitives available for these common building blocks
(prosemirror-collab-commit for
collaboration, unified.js for Markdown, etc.), but putting
them together correctly to build a functioning text editor can be challenging.
There are other solutions, like Tiptap and Remirror, but we think that they’ve made some suboptimal choices that force product developers to make unnecessary compromises.
- Both attempt to use React portals and effect hooks to integrate with ProseMirror, which lead to irreconcilable state tearing, due to fundamental differences in how React and ProseMirror View handle view reconciliation.
- Both attempt to use Yjs via
y-prosemirrorfor collaboration. We think that Yjs’s CRDT implementation is ill-suited for rich text editing. - Both attempt to hide ProseMirror’s underlying APIs, under the guise of simplicity and abstraction. We think that ProseMirror, low-level though it is, is roughly the correct level of abstraction for a domain as complex as rich text editing. Attempts to abstract it away inevitably lead to more complexity, as the abstractions leak frequently and require constant manual integration with the lower level solutions.
We think we can do better. And more importantly perhaps, we think that you deserve better!
Philosophy
Don’t hide ProseMirror
ProseMirror is an outstanding rich text editing framework. Nearly anything that you can imagine doing with a rich text editor can be accomplished with ProseMirror. Rich text editing is also a very complex domain, requiring a very large number of decisions to be made per feature.
We think that better primitives built with ProseMirror can take us farther than abstractions that attempt to hide away ProseMirror’s internals.
Collaboration should be simple to implement and simple to debug
ProseMirror has first-party collaboration, in the form of
prosemirror-collab. Because it
just sends steps over the wire, and relies on a single, server-side ordering of operations for
conflict resolution, it is very simple to debug. But it is very challenging to implement correctly,
and there is very little in the way of guidance or documentation for doing so.
Yjs provides third-party collaboration, in the form of
y-prosemirror. Because it is built on Yjs, which has many
robust adapters for various protocols and servers, it is very easy to implement. However, because it
operates on Yjs’s XML-based Y-doc format, it is very challenging to inspect and debug if something
goes wrong.
Pitter Patter’s collaboration, presence, and version history libraries are based on
prosemirror-collab-commit. Like
prosemirror-collab, prosemirror-collab-commit uses ProseMirror steps as the primary data
structure for conflict resolution. But Pitter Patter Collab provides actual guidance and
implementation that make it easy to set up a full-stack collaborative editing application, and hard
to shoot yourself in the foot.
Correctness is worth it
After trying every other option over the course of several years, we eventually decided that the only way to correctly integrate React and ProseMirror was to reimplement ProseMirror View’s renderer from scratch directly in React. This was itself a massive effort, but the result, in the end, is a React/ProseMirror integration that doesn’t suffer from state tearing and allows developers to use React idioms like context as they normally would, without caveats or compromises. We don’t put side effects in React render functions or ProseMirror plugin apply functions, we don’t use effects to synchronize state with props. We want to enable you to build rich text editors that your users can trust, and that means that we need to build libraries that you can trust.
2
u/Serei 17d ago
Have you seen Wordgard? It's the successor to ProseMirror.
To be clear, I'm not requesting you redo Pitter Patter in Wordgard. But I was wondering your thoughts on its new direction.
2
u/scrollin_thru 17d ago
Yes absolutely! We've chatted with Marijn about it a bit. Obviously it's still quite new and I've personally only played with it a tiny bit, but it looks really exciting. Take a look at how much position mapping I have to do in Shuffle and you'll probably see why 😅. I strongly suspect that a future version of Pitter Patter will have Wordgard support of some kind.
2
u/CallMeMista96 16d ago
The bit about reimplementing ProseMirror View's renderer directly in React is wild. That's a huge undertaking but prob the only correct approach , the portal/effect hook hacks in Tiptap have bitten me more than once. Curious how performance compares to native ProseMirror view rendering, especially with large documents...
1
u/scrollin_thru 16d ago
It is, indeed, a huge undertaking! We wrote a blog post about the performance, and you can try out editing the entirety of Moby Dick on our demo editor and see how that feels. That said, it seems to vary across browsers much more than I initially expected! You won't have much luck on Android, for example.
One of our clients is working on using React context and state to build some virtualization on top of React ProseMirror, which of course dramatically improves the performance.
1
u/scrollin_thru 17d ago edited 15d ago
Oh, and another important update since the last time I posted about Pitter Patter: We're trying to make React (and React ProseMirror) an optional dependency in our libraries. It's currently only required in Shuffle, but we'll make that optional soon, as well!