|
1 | | -# Pitter Patter |
| 1 | +<p align="center"> |
| 2 | + <a href="https://pitter-patter.dev/"><img src="https://raw.githubusercontent.com/handlewithcarecollective/pitter-patter/refs/heads/main/readme-assets/pitter-patter-logo.png" alt="" height="75"/></a> |
| 3 | +</p> |
2 | 4 |
|
3 | | -A React ProseMirror toolkit that works _with_ you. |
| 5 | +<h1 align="center">Pitter Patter</h1> |
| 6 | + |
| 7 | +<p align="center"> |
| 8 | + <strong>An open source collaborative rich editing toolkit.</strong> |
| 9 | +</p> |
| 10 | + |
| 11 | +## What is Pitter Patter? |
| 12 | + |
| 13 | +Pitter Patter is an open source collaborative rich text editing toolkit, built with React and |
| 14 | +ProseMirror. |
| 15 | + |
| 16 | +## Who builds it? |
| 17 | + |
| 18 | +That would be us, [Handle with Care](https://handlewithcare.dev/). We’re a cooperatively owned |
| 19 | +product development collective. That means that we don’t have employees or managers — we’re all |
| 20 | +equal owners and have equal decision-making power. |
| 21 | + |
| 22 | +We’re funded in two ways: |
| 23 | + |
| 24 | +1. Companies pay us to help them with their collaborative rich text editors, or other complex |
| 25 | + software. |
| 26 | +2. Sponsors contribute money to directly support the development of Pitter Patter and our other open |
| 27 | + source software. |
| 28 | + |
| 29 | +## What is it? |
| 30 | + |
| 31 | +Pitter Patter is a suite of open source libraries for building collaborative rich text editors with |
| 32 | +React and ProseMirror. We see it as our job to provide turnkey solutions for the hard parts of rich |
| 33 | +text editing — like collaboration, presence, and version history — so that you can focus on building |
| 34 | +the editor experience that your users need. |
| 35 | + |
| 36 | +## Why does it exist? |
| 37 | + |
| 38 | +We spend a lot of time thinking about and working on rich text editors. In particular, we spend a |
| 39 | +lot of time helping companies solve the same problems over and over again. There are usually great |
| 40 | +primitives available for these common building blocks |
| 41 | +([`prosemirror-collab-commit`](https://github.com/stepwisehq/prosemirror-collab-commit) for |
| 42 | +collaboration, [unified.js](https://github.com/unifiedjs/unified) for Markdown, etc.), but putting |
| 43 | +them together correctly to build a functioning text editor can be challenging. |
| 44 | + |
| 45 | +There are other solutions, like [Tiptap](https://tiptap.dev/) and |
| 46 | +[Remirror](https://www.remirror.io/), but we think that they’ve made some suboptimal choices that |
| 47 | +force product developers to make unnecessary compromises. |
| 48 | + |
| 49 | +1. Both attempt to use React portals and effect hooks to integrate with ProseMirror, which |
| 50 | + [lead to irreconcilable state tearing](https://handlewithcare.dev/blog/why_i_rebuilt_prosemirror_view/), |
| 51 | + due to fundamental differences in how React and ProseMirror View handle view reconciliation. |
| 52 | +2. Both attempt to use [Yjs](https://github.com/yjs/yjs) via |
| 53 | + [`y-prosemirror`](https://github.com/yjs/y-prosemirror) for collaboration. We think that Yjs’s |
| 54 | + CRDT implementation is |
| 55 | + [ill-suited for rich text editing](https://www.moment.dev/blog/lies-i-was-told-pt-2). |
| 56 | +3. Both attempt to hide ProseMirror’s underlying APIs, under the guise of simplicity and |
| 57 | + abstraction. We think that ProseMirror, low-level though it is, is roughly the correct level of |
| 58 | + abstraction for a domain as complex as rich text editing. Attempts to abstract it away inevitably |
| 59 | + lead to _more_ complexity, as the abstractions leak frequently and require constant manual |
| 60 | + integration with the lower level solutions. |
| 61 | + |
| 62 | +We think we can do better. And more importantly perhaps, we think that you _deserve_ better! |
| 63 | + |
| 64 | +## Philosophy |
| 65 | + |
| 66 | +### Don’t hide ProseMirror |
| 67 | + |
| 68 | +ProseMirror is an outstanding rich text editing framework. Nearly anything that you can imagine |
| 69 | +doing with a rich text editor can be accomplished with ProseMirror. Rich text editing is also a very |
| 70 | +complex domain, requiring a very large number of decisions to be made per feature. |
| 71 | + |
| 72 | +We think that better primitives built with ProseMirror can take us farther than abstractions that |
| 73 | +attempt to hide away ProseMirror’s internals. |
| 74 | + |
| 75 | +### Collaboration should be simple to implement _and_ simple to debug |
| 76 | + |
| 77 | +ProseMirror has first-party collaboration, in the form of |
| 78 | +[`prosemirror-collab`](https://code.haverbeke.berlin/prosemirror/prosemirror-collab). Because it |
| 79 | +just sends steps over the wire, and relies on a single, server-side ordering of operations for |
| 80 | +conflict resolution, it is very simple to debug. But it is very challenging to implement correctly, |
| 81 | +and there is very little in the way of guidance or documentation for doing so. |
| 82 | + |
| 83 | +Yjs provides third-party collaboration, in the form of |
| 84 | +[`y-prosemirror`](https://github.com/yjs/y-prosemirror). Because it is built on Yjs, which has many |
| 85 | +robust adapters for various protocols and servers, it is very easy to implement. However, because it |
| 86 | +operates on Yjs’s XML-based Y-doc format, it is very challenging to inspect and debug if something |
| 87 | +goes wrong. |
| 88 | + |
| 89 | +Pitter Patter’s collaboration, presence, and version history libraries are based on |
| 90 | +[`prosemirror-collab-commit`](https://github.com/stepwisehq/prosemirror-collab-commit). Like |
| 91 | +`prosemirror-collab`, `prosemirror-collab-commit` uses ProseMirror steps as the primary data |
| 92 | +structure for conflict resolution. But Pitter Patter Collab provides actual guidance and |
| 93 | +implementation that make it easy to set up a full-stack collaborative editing application, and hard |
| 94 | +to shoot yourself in the foot. |
| 95 | + |
| 96 | +### Correctness is worth it |
| 97 | + |
| 98 | +After trying every other option over the course of several years, we eventually decided that the |
| 99 | +only way to correctly integrate React and ProseMirror was to |
| 100 | +[reimplement ProseMirror View’s renderer from scratch directly in React](https://handlewithcare.dev/blog/why_i_rebuilt_prosemirror_view/). |
| 101 | +This was itself a massive effort, but the result, in the end, is a React/ProseMirror integration |
| 102 | +that doesn’t suffer from state tearing and allows developers to use React idioms like context as |
| 103 | +they normally would, without caveats or compromises. We don’t put side effects in React render |
| 104 | +functions or ProseMirror plugin apply functions, we don’t use effects to synchronize state with |
| 105 | +props. We want to enable you to build rich text editors that your users can trust, and that means |
| 106 | +that we need to build libraries that _you_ can trust. |
4 | 107 |
|
5 | 108 | <!-- NOTE: This section is autogenerated. Do not manually edit.--> |
6 | 109 | <!--sponsorsstart--> |
|
0 commit comments