From TinyMCE to Lexical and TipTap Rich Text Editor

1. Background

Around the same time last year, I started building the Chrome extension Ultra Notes

The extension is an advanced note-taking tool that allows users to take notes, and therefore, one of its key features is to support rich text editing. Initially, I used TinyMCE as the rich text editor (RTE) solution for the extension. The bundle size is quite large, 10mb, but it works perfectly fine in version 1.x of the extension. However, when I started adding more features and scaling up the project, I found that TinyMCE was not the best choice. It leads me to disable the rich text editor in the extension version 2.x and look for a better solution. I were introduced to Lexical a couple of months ago and quickly fall in love with it. I had spent two weeks to integrate Lexical into the extension just to find out that it has the same issue to TinyMCE: they don't support Shadow DOM very well, which is crucial for Chrome extensions. It was quite disappointed because of the wrong investment (two weeks of my leisure time). On the other hand, I learned a lot about today rich text editor solutions and how powerful they are. Finally, I found TipTap. Tt works perfectly fine with the extension version 3.x. The bundle size now is around 4.2mb, or 1mb after zipped. It is lightweight, simple, and Shadow DOM supported.

Below is the comparison of the three rich text editors and a simple guide to integrate them into a Chrome extension written by Svelte.

2. TinyMCE

The Ultra Notes is free to use, non-commercial, at the time this post is written, so I have no problem with the TinyMCE license. But it is worth to learn a little bit about licenses of any open-source solutions you are using in your projects. In this case, it is GPLv2 for TinyMCE, v.7.3.0, at the time of writing this post.

info

The key features of GPLv2 are:

  • You need to open your source code if you distribute your software, and maintain the same GPLv2 license for your software.
  • You can use the software for commercial purposes, but you need to follow the license terms, and retain the copyright notice of the GPLv2 software used in your software.

For example, you may see the TinyMCE editor embedded in your software display a message like "Powered by TinyMCE" or "TinyMCE" in the footer of the editor. This is to retain the copyright notice of the software used in your software. And you are not allowed to hide it.

I have no plan to publish the source code of the extension, so the GPLv2 TinyMCE doesn't work for me. Certainly, I can pay or subscribe to the TinyMCE premium version to remove the restriction, but it is not worth for a small and free project like Ultra Notes.

Integrate TinyMCE to a Chrome Extension (Svelte)

The official guide to create a TinyMCE Svelte Wrapper is available at https://github.com/tinymce/tinymce-svelte, that can be used for any Svelte web application. However, to use it in a Chrome extension, we need to make some additional changes.

// Firstly, we need to install the TinyMCE package from npm in 
// packages.json
"devDependencies": {
  "tinymce": "^7.3.0",
}

The advantage of using TinyMCE is everything is cooked for you, and you can use it right away. However, to use it, we must include the whole bundle of TinyMCE, as a static copy, in the extension. In Svelte, I copy the artifacts of TinyMCE to the public folder, load the scripts and css, and then initialize the editor in a wrapper (Svelte component).

// vite.config.ts
// ... other imports
import { viteStaticCopy } from 'vite-plugin-static-copy'

const config: UserConfig = {
  plugins: [
    // ... other configs
    viteStaticCopy({
      targets: [
        {
          src: 'node_modules/tinymce/*',
          dest: 'tinymce'   // copy to the dist/tinymce folder at the build time
        }
      ]
    }),
  ],
  // ... other configs
};

export default config;

Don't forget to allow tinymce to be accessible in a Chrome extension by adding the following line to the manifest.json file:

web_accessible_resources: [{
  matches: ["<all_urls>"],
  resources: ["tinymce/**"],
}],

Here we custom the TinyMCE Svelte wrapper component, that allows the editor to change its Light/Dark theme and to load the initial content from outside.

And finally use it in the Chrome extension popup or sidePanel page.

<TinyMCE
  scriptSrc={chrome.runtime.getURL('tinymce/tinymce.min.js')}
  bind:value={currentNoteContent}
  conf={tinyMceConfig}
/>

Why TinyMCE doesn't fit in a Chrome Extension

Besides the drawbacks. of the large bundle size, the main reason why TinyMCE doesn't fit in a Chrome extension is that it doesn't support Shadow DOM very well. If you are building a Chrome extension that works as an isolated web application, you don't face this issue. For example:

However, the problem arises when you want to make your extension work with the webpage's content, embedding a UI into the webpage, and therefore improve the user experience. For example, in my use case, I want the Ultra Notes to popup on any webpage, and users can drag and drop it to any position on the webpage. Using this technique, users can take notes while reading an article, watching a video, or browsing a website, without switching tabs or clicking on the extension icon, which will be closed when the popup loses focus.

image

This is where the content-script and Shadow DOM comes into play, and unfortunately TinyMCE doesn't support it. https://www.tiny.cloud/docs/tinymce/latest/shadow-dom/

image

3. Lexical Rich Text Editor by Meta (Facebook)

I have to admit that Lexical is a great rich text editor, and I love it. It is simple, lightweight, maintained by a big name (Meta, formerly Facebook), and offers the MIT license, which is very friendly for any project.

Users of software using an MIT License are permitted to use, copy, modify, merge publish, distribute, sublicense and sell copies of the software.
Ref. https://pitt.libguides.com/openlicensing/MIT

image

But it doesn't mean that Lexical is the best choice for all projects. In the use case of inject content-script into a webpage as I mentioned above, Lexical has the same issue as TinyMCE: it doesn't support Shadow DOM very well.

My experience with Lexical is as below:

  • I play around with the Lexical basic functions and features, and it seems to work fine as a headless editor, pure javascript in a Svelte component (Chrome extension).
  • I didn't check carefully with the Shadow DOM, but I started to port (rewrite components) the svelte-lexical project to my project.
    • I copy and modify components, styles, and scripts to fit my project.
    • I make the component isolated in a library, with intention to reuse each component independently like the shadcn-svelte project.
  • It took me two weeks to port the Lexical to my project to realize that Selection API doesn't work with the Shadow DOM.
  • Basically, Lexical partially work with Shadow DOM. But it is nothing if the Selection API doesn't work, because most of everything we do with a rich text editor is built on a Selection.

note

The svelte-lexical make components depend on the global CSS, assets (icons) and other components. To use its component, it requires us to copy the whole project to our project, and then modify the components to fit our project. So my idea is rewriting the components, styles, and scripts to make them independent, and then simply import only components that we need. The idea is similar to the shadcn-svelte project.

3. Tiptap Editor

And then I found Tiptap, a great rich text editor that works perfectly fine with the Shadow DOM. And MIT license, available for any project.

Tiptap is a headless editor, similar to Lexical, that we need to build the UI by ourselves. It means that following the official guide at https://tiptap.dev/docs/editor/getting-started/install/svelte, what you receive is a very basic plain editable area. To make it a rich text editor, we need to build the UI, toolbar, and other features by ourselves.

image

info

4. Conclusion

In summary, this article discusses three open source Rich Text Editors (RTE) solutions, but there are many ones available, and each has its own pros and drawbacks, that we choose the right one built on our project requirements.

TinyMCELexicalTiptap
LicenseGPLv2 (your source code must be public)MIT (free to use in most of cases)MIT (free to use in most of cases)
Web app
Chrome extension (popup & sidePanel)
Shadow DOM
Ready to use with UI
Easy to custom
Small bundle size

Related Posts

Ultra Notes, a simple note taking Chrome extension built with Svelte

The journey began when I sought a straightforward note-taking app for my everyday needs. Several months ago, I experimented with various online note-taking platforms for work. However, my boss, upon

Read More

Multi Highlight and Replace with Regex in X-Word-Replacer

[Regular Expression (RegEx)](https://www.geeksforgeeks.org/write-regular-expressions/) is a powerful tool for searching and replacing text that I have implemented in the X-Word Replacer since its fi

Read More

How To Build a Chrome Web Extension with Svelte

## Background There are many possible ways to build a Chrome extension (or Firefox addon, or web extension in general). * The simplest way is to use vanilla JavaScript, HTML, and CSS following the o

Read More