Jump to content

IC5: The New Editor

In 2024, a secure WYSIWYG Editor has become a complex intricate thing.

Copy/paste bundle files have largely been phased out in favor of complicated NPM repos and build tools. What was more or less just "HTML Manipulation" has evolved to abstract content models with dynamic rules on how to actually render the content to HTML. Then, for kicks, throw in the requirement that this editor needs to work in non-standard cases like the drag and drop page builder and live topics. The solution for Invision Community 5 is a new, custom, state of the art Editor built using ReactJS and Tiptap.

In this article, I'll cover high level advantages, technical highlights and what's possible with 3rd party extensions. 

If you want to know more about the editor functionality, then see my other blog here.

This story begins after we switched to CKEditor5 over a year ago. It is a decent product, but it had several serious limitations in a distributed platform like Community. We compared just about every editor currently on the market, and even considered building an editor completely from scratch, and ultimately landed on creating a custom editor using ReactJS and Tiptap.


A lightweight package

The v5 editor bundle size is small, coming to about 2mb in total (and it's split into separate chunks loaded ad-hoc, each one cached in the browser, so in practice much less data actually "loads"). After optimizing and tweaking CKEditor5, the smallest we could get it was over 50mb; now some build tool experts could probably wrangle that cke package to a smaller size, but I doubt it'd come anywhere close to Tiptap with all the features we added.

It's also worth noting that, during operation, Tiptap had by far the lowest impact on browser memory consumption, and stubborn memory leaks never came up with this architecture. Part of this comes from taking advantage of ReactJS's stateful nature, but I think the most significant thing is that Tiptap doesn't have loose logic hanging around.


How a modern WYSIWYG works (the short version)

As I previously alluded to, editors today don't directly manipulate HTML at all. Instead, they store a Content Model according to a Schema.

Different platforms may use different terms, but the content schema consists of the following Entities

  • Nodes - Actual containers to put content in. Think blockquotes, headings or lists. Nodes are then stored in the document in a tree
  • Text and Inline Nodes - Text is, well, text. It is exclusively just a string of characters; one way to think of it is, if it can go in a text message, it's text. Inline nodes, on the other hand, are not true "characters" but displayed inline with characters. Think custom emoticons and mentions.
  • Marks - Styles and/or data that is applied over a range of text. Marks are similar to Nodes except that marks can overlap. For example, it if text is italic and bold, it makes no difference if it's bold inside italic or vise versa. Moreover, it it most accurate to simply say that text is just both italic and bold; this data is stored in Marks. In old editors, like CKEditor4, this was often just thrown into a "style" attribute which led to all sorts of issues and overhead processing. (These are not what CKEditor calls "marks" FYI, they call this concept "text attributes", in case anyone has existing CKE knowledge)

All the above entities rely on Converters to generate them from HTML and then reconvert back into HTML. When the editor starts, it uses those converts to convert the initial data into the Content Model. Then, any change is made to the Content Model directly, which is continuously being converted back into HTML for the user to see.

Security and Consistency

The Schema and architecture add a layer of complexity but provide much better security, consistency and reliability because it only allows whitelisted content structures. For example, if I inserted something with the attribute style="color:white; transform: rotate(45deg)", the transform and color will be stripped out unless there's an existing converter to convert it to a Mark.

Another example good example is simple bold styling: I can define several converters to parse the bold Mark: one for <b> tags, one for <strong> and one for style="font-weight: bold", and have them all convert back into a <strong> tag.

Lastly, this gives us the assurance that, for any style, there is a user interface to manipulate and manage it. For this reason, source editing was removed because you won't ever need to fall back to source editing.

I could go on for hours (seriously, don't get me started) but that's more than enough context to understand the rest of this article.


Extending the Editor in Invision

Another limitation of CKEditor5 in a distributed platform, where individual admins may want to customize, is that all Plugins have to be present at build not at runtime. This would pretty much mean no extending the editor, just rearranging the toolbar and flipping certain builtin features on and off.

Fortunately, in Tiptap we were able to extend their internal node, mark and extension APIs. We'll have a more complete dev guide, but essentially all nodes, marks and extensions, which I'll just call "extensions" collectively, are rolled into the existing app system. There are 2 reasons we did this instead of just "buttons" like Invision 4, where you can just upload the CKEditor4 plugin zip file:

  1. It relies on using PHP to parse JavaScript to get things like the button title and stuff. If you create a valid plugin but define things in a slightly different way than expected, such as adding the name to the wrong line in the file, the plugin upload fails.
  2. Plugins/extensions are not buttons. Many CKE plugins have multiple buttons and many have none. This make management difficult because sometimes there isn't a button to remove and you have to reset all plugins, or you want to remove just one and 5 others disappear with it. Also, mostly all plugins add a new type of content; this type of content needs to be whitelisted

Now, you may be thinking, "like the editor itself, won't the server side HTML Parser will strip any content that is not whitelisted"? Well, the answer is yes and we added a new AdminCP Dev Center tool to create Parser Whitelist Rules inside Invision Community Apps (again more on all this later). With the coupling of the Parser Whitelist and Editor Extensions, admins can rest assured that their editors will just "work" when you install Extensions.

If you want to prematurely prepare to create Invision Community 5 Editor Extensions, have a look at Tiptap's Dev API Documentation, specifically the methods Node.create(), Mark.create() and Extension.create(). We've added wrappers for those three methods, in addition to an interface to add and position buttons in the toolbar.

Please let me know if you have any questions!


Recommended Comments

There are no comments to display.

  • Create New...