Stesura

Why we left Tiptap

Edoardo Caratti,•4 min read

Why we left Tiptap.

We started out with Tiptap, and it was great. Until it wasn’t.

Tiptap allowed us to move fast: the API is easy and well documented, and it was a great fit for our use case at the time. It is the easiest way to get started with ProseMirror. No disrespect to Marijn, ProseMirror’s documentation is excellent once you get past the learning curve, but it’s a steep one. Tiptap also gave us a lot of insight, and a lot of code to check out to understand how things work under the hood. So this isn’t a hit piece. It’s the story of how our editor outgrew it.

React first, and performance.

Our use case required a large number of nodeviews, and that’s where things started to hurt. Tiptap mounts each React nodeview through a portal, with its own wrapper element. In our experience that overhead wasn’t ideal for performance, and it added up quickly on a document a couple hundred pages long.

react-prosemirror was a godsend. It lets us have as many nodeviews as we need with minimal overhead: under the hood, react-prosemirror already renders every node as a React component, so writing our own adds very little on top. With overhead that low we could pretty much create nodeviews whenever we saw it convenient, instead of weighing each one against its cost.

Shane has done phenomenal work on react-prosemirror (he explains why he rebuilt ProseMirror’s view  here), and we’re grateful for it. Its performance has been mind-blowing, and we couldn’t have done what we did without it.

Two APIs are one too many.

With Tiptap you end up using two APIs: the Tiptap API and the ProseMirror API. Tiptap’s documentation is a double-edged sword. It’s so good that it keeps you in their API for too long, and you’ll fail to grasp the basics of ProseMirror: the data model, the schema, the transactions, the plugin state and state updates. All things you will need to understand once you’re building features at this level.

Frontend and backend got tangled.

In our experience, Tiptap didn’t separate frontend and backend well enough for us. Extensions mix nodeviews and plugins together, which made it really painful the day we wanted to have our server operate on the documents. A server needs the schema and the commands, not the React components that come bundled with them.

Who’s doing what?

The extension data model is great for extensibility and convenient for small editors, but it made it a lot harder to know what’s doing what. We found it much easier to have a separation of concerns between schema/commands on one side and plugins on the other. That’s especially true since we rely heavily on appendTransaction, and you really want to know where they’re all at to keep an eye on them. Else, good luck debugging.

Should you be using Tiptap?

If you need an easy text editor, yes, without hesitation. It’s still the fastest way to get something good in front of users. For a word processor, we wouldn’t recommend it. We needed nodeviews with minimal overhead, a clean split between what runs in the browser and what runs on the server, and a codebase where we can see every appendTransaction at a glance.

Tiptap does seem to want to go that way: they now ship a paid Pages extension and official DOCX import/export. Our opinion when we left was that trying to serve everyone would hold them back for this use case. A word processor is not something you arrive at, it’s something that is built from the ground up as such.

Leaving Tiptap meant learning ProseMirror properly, which we were going to have to do anyway. We just wish we’d done it sooner.