I finally decided to stop building Writings with Flutter Web when I got closer to its rich text formatting features. At the beginning, I didn't have in mind to build Writings with a rich text support. I wanted to build a small tool to write and export images only. But as things became more interesting and because I started building Writings to solve my own problem, rich text formatting went into the pipeline.
Flutter Web is a great tool if you are not picky about things. I wrote about it in another article. Once your appetites grow, you will need to either reconsider it or try hard to achieve what you need. For me, it was the first case. I moved to Next.js. The only problem was that I was learning Next.js as I was migrating the features. Eventually, I migrated 90% of the codebase and that gave me the opportunity to start using all the great things that the
After TipTap, I tried two more rich text libraries. All three of them are open source and free of charge. In this article, I will share my experience with them.
The Rich Text building frameworks
contentEditable? It's an attribute that you can assign to an HTML element to indicate that the element itself is editable. You can do this over practically anything.
But understanding the theory doesn't mean you should take this road. If you want to implement things fast, you want to go with something that maybe is using
contentEditable under the hood, but you don't have to implement any of it. One of those implementations is a framework for creating rich text editors called ProseMirror.
You can use ProseMirror to create editors, gain control over them and debug potential issues along the way. ProseMirror provides rich text functionalities on top of the contentEditable attribute.
It's an abstraction if you will.
TipTap - the headless WYSIWYG editor
TipTap on the other hand is a library built using ProseMirror to enable rich text formatting. It's an abstraction over an abstraction.
To integrate TipTap within your app all you need to do is:
- install the npm package
- define the component that will be the editor
- add the component where needed.
In three easy steps, you will have robust and elegant rich text formatting features for your app. As TipTap is open source, there is significant support from the community, which is the best validator of a technology.
The problems for me showed when I started using the editor for longer and more complex texts. It has to do with my use case and I would not say that TipTap does not scale well, but if you want to have more atomic control over what the user is typing, you might notice performance issues. In my case, I was live listening for the changes in the text, converting the text to plain text as the user was typing and doing more operations over the converted text. I was implementing a statistics feature that would show the number of words, time to read, etc. The moment I started live listening for these changes, TipTap performance dropped significantly.
The alternative was to perform the statistics operation after the user saved the document or seconds after they stop typing. Without much hesitation, I started checking out other alternatives.
Another negative aspect is that TipTap does not support Markdown. And I wanted to have Markdown support since the beginning. Frameworks influence product decisions and in this case, I didn't want to overthink my features because of a framework limitation.
- It is really simple to integrate
- it's headless (you just implement it, everything is integrated, no need for additional CSS only until you don't really need it)
- has a great community and with that amazing support
- has a satisfactory number of plugins to use out of the box
- you cannot customize existing plugins easily
- performance issues for larger documents with a lot of formatting
- no markdown support
Still, if you need an easy and non-complex RTF editor, give Tip Tap a try. It's still an amazing piece of software.
Slate - the bloated baboon
Slate is another framework similar to ProseMirror. A framework to build rich text editors. It's never good to start being negative, but there are two main problems with Slate: it's under-documented and (because of that) difficult to integrate.
Installation and integration are very similar to what I had to do with TipTap: install, create the component, and use it.
The tricky part was to find out how to enable the more advanced features. Those around rich text formatting. Namely, by default, if you create the editor it might not have rich text support. That is why Slate is a framework, you can build anything, pretty much, but you have to do it yourself. And, as I said, the documentation sometimes was just not there.
One thing I noticed and I think it's a technical problem of their website is that, while you are browsing the documentation of Slate, you can select which version that doc is about. If you don't see it, and you end up reading a document for a plugin version different than what you have just installed, things will become grim. For me, that meant spending few hours trying to understand why an import does not resolve. It's because I was not reading the latest documentation.
The moment I figured out how to enable rich text formatting, I wanted to play with things like an imperative change of text formatting. Something like change the selected text to bold on press of a button. Again, this took a significant amount of time to enable.
The final decision came when I tried to serialize/deserialize HTML. At one point, I had to use a manual parser that would parse the DOM tree and apply the necessary formatting and styles. I gave this a try, as I found online snipped that I reused it. But then I wanted to interact with that code which was almost impossible for me, and eventually, I broke everything.
- Simple to integrate
- it's really powerful and you can have full control over what your RTF features are about
- has a good number of plugins to use out of the box
- difficult to understand documentation
- variety of versions for the libraries and documentation
- difficult to implement plugins/features on top (only if you are not an expert)
- manual serialization/deserialization
Then, I decided to look for more.
As I was whining over Slate on Twitter, someone recommended Plate. It is an out-of-the-box RTF editor built on top of Slate.
Finding Plate for me was the perfect balance between performance and time to implement. The greatest value is that it comes with a lot of plugins that you can use right away. It still has the problem with the documentation, I guess inherited from Slate, but it's far from its complexity. And that is a good enough trade-off.
For example, if I want to deserialize an HTML, there is a plugin for it that I have to install and then use. Then similarly to TipTap, performance became a problem as I was doing the same listen → deserialize pattern. But after restructuring the code, Plate started behaving as I expected. No performance issues, just a slight change of the use-case, and lots of plugins to use (my favorite is the Balloon Toolbar —part of the plate-ui-toolbar plugin.
- solid and powerful as it's built on top of Slate
- good enough documentation
- great list of preprepared plugins
- serialization/deserialization is bliss
- not very clear documentation
- examples sometimes are outdated
I was recommended to check a few more, paid libraries. Professional and solid. But at the moment I don't need them. I found a good balance in Plate and with that, I can continue building my features. The obvious winner for me was Plate.
If you are building an app that has integrated RTF support, besides my empathy for you, I want to emphasize my recommendation to try all of these libraries, in the order given. If one of them works for you, you have the problem solved.
Always open for discussion, reach out on Twitter anytime.