Exploring Rich Text Editor Engines

In the previous article, we introduced the basic concepts of rich text and its development history. In this article, we will explore the currently mainstream open-source rich text editor engines. The most widely used rich text editor currently is the L1 rich text editor, which can meet the needs of the vast majority of use cases. As a result, many excellent open-source rich text engines have been born. These include standalone engine editors such as Slate.js, partially out-of-the-box functional editors like Quill.js, and secondary development based on these engines such as Plate.js. This article mainly introduces three editing engines: Slate.js, Quill.js, and Draft.js.

Slate.js

Slate is a rich text core that only provides an engine. Simply put, it does not provide various rich text editing functions by itself, and all rich text features need to be implemented through its provided API. Even its plugin mechanism needs to be expanded by oneself, so using Slate to build a rich text editor can be highly customizable. Although the documentation for Slate is not particularly detailed, its examples are very rich. The documentation also provides a tutorial as a starting point, which is quite friendly for beginners. Speaking of which, if only an engine is provided, no one knows how to use it, haha.

In the Slate documentation, there are descriptions of the design principles of the framework:

  • Plugins are first-class citizens. The most important part of Slate is that plugins are first-class citizen entities, which means you can completely customize the editing experience to build complex editors like Medium or Dropbox without struggling with the default settings of the library.
  • Lean schema core. The core logic of Slate imposes very few presets on the structure of your edited data. This means that when you build complex use cases, you will not be hindered by any pre-existing content.
  • Nested document model. The model used by Slate documents is a nested, recursive tree, just like DOM. This means that for advanced use cases, it is possible to build complex components such as tables or nested references. Of course, you can also use a single-level structure to keep it simple.
  • Same as DOM, Slate's data model is based on DOM. The document is a nested tree, using text selections and ranges, and exposes all standard event handling functions. This means that advanced features such as tables or nested references that can be done in DOM can also be done in Slate.
  • Intuitive commands. Slate documents execute commands to edit. It is designed to be advanced and very intuitive for editing and reading, so that customized features are as expressive as possible, greatly improving your understanding of the code.
  • Collaborative data model. The data model used by Slate, especially how operations are applied to the document, is designed to allow collaborative editing at the highest level, so if you decide to implement collaborative editing, you don't have to consider a complete refactor.
  • Clear core division. The structure of prioritizing plugins and a lean core makes the boundary between the core and customization very clear, meaning that the core editing experience will not be troubled by various edge cases.

Example

The data structure of a text sample looks like this:

[
  {
    children: [
      { text: "Like this" },
      { text: "a text sample", italic: true },
      { text: "of" },
      { text: "data structure", bold: true },
      { text: "as shown below." },
    ],
  },
];

Advantages

  • Highly customizable, almost all rich text display layer functions need to be implemented independently.
  • Very lightweight, it has only one core, and the functionality is implemented as needed, making the volume controllable.
  • Implemented OP-optimized incremental changes for collaboration during the architecture design.
  • The data structure is a nested JSON structure similar to DOM, making it intuitive and easy to understand.

Disadvantages

  • Still in the Beta stage, there may be major API changes. For example, version 0.50 was completely refactored.
  • Although it has collaborative design, it lacks direct support for OT or CRDT algorithms for transformation operations.
  • The architecture with only the core is also a disadvantage, as all functionality needs to be implemented independently, resulting in higher cost.
  • The new version of Slate does not have a schema, which means that Normalize needs to be implemented independently.

Reference

  • Official documentation for slate can be found at https://docs.slatejs.org/.
  • Check out some examples from the slate official website at https://www.slatejs.org/examples/richtext.
  • Personally implemented rich text editor based on slate can be accessed at https://github.com/WindrunnerMax/DocEditor.
  • Get started with a ready-to-use rich text editor based on slate at https://plate.udecode.io/docs/playground.
  • For reference on collaborative implementation of OT with slate, visit https://github.com/solidoc/slate-ot.
  • Visit https://github.com/pubuzhixing8/ottype-slate for OTTypes reference for slate.
  • Explore collaborative implementation with CRDT for slate at https://github.com/humandx/slate-automerge.
  • For information on combining slate with YJS collaboration, refer to https://github.com/BitPhinix/slate-yjs.
  • Excellent articles on slate can be found at https://github.com/yoyoyohamapi/book-slate-editor-design.

Quill.js

Quill is a modern rich text editor with excellent compatibility and powerful extensibility, offering some out-of-the-box features. It was open-sourced in 2012 and brought many new features to the rich text editor, making it one of the most popular open-source editors with a rich ecosystem. You can find numerous examples, including comprehensive collaborative instances, on platforms like GitHub.

The design principles of the framework are described in the documentation:

  • API-driven design: All of Quill's features are implemented through the API and data changes can be intuitively obtained through the API. It is built on a text-centric API, eliminating the need to parse HTML or DIFF DOM trees, making its functionality more flexible and highly extensible.
  • Custom content and formats: Quill provides its document model as a powerful abstraction of the DOM, allowing for expansion and customization, with data structures such as Blots, Parchment, and Delta taking center stage.
  • Cross-platform: Quill has excellent compatibility and can run in the same way on older browser versions, ensuring consistent views and interactions across different browsers, as well as desktop and mobile devices.
  • Ease of use: Quill comes with some out-of-the-box features that are sufficient if customization is not required. Additionally, it offers high extensibility for customizing various functions.

Example

The data structure for a piece of text is as follows:

{
  ops: [
    { insert: "This" },
    { insert: "is a piece of text", attributes: { italic: true } },
    { insert: "with", },
    { insert: "customized content", attributes: { bold: true } },
    { insert: "as shown.\\n" },
  ],
};

Advantages

  • API-driven design with rich support for basic functionality and extensibility.
  • Implementation of a view layer independent of the framework, making it easy to use with Vue or React.
  • Use of a more intuitive Delta to describe data structures, implementing logic for operation transformation and facilitating collaboration.
  • Rich ecosystem with numerous plugins and design schemes to reference, particularly in terms of its support for collaboration.

Disadvantages

  • The Delta data structure is flat, making it cumbersome to implement features like tables.
  • Quill 2.0 has been in development for a long time and has not been formally released yet. The current version, 1.3.7, has not been updated for many years.
  • While Quill comes with a rich set of features, this also results in a larger package size.

Reference

  • Official documentation for Quill can be found at https://quilljs.com/docs/quickstart/.
  • Check out some examples from the Quill official website at https://quilljs.com/standalone/full/.
  • For Delta design and implementation in Quill, refer to https://quilljs.com/docs/delta/.
  • Explore OTTypes reference for collaborative implementation with Quill at https://github.com/ottypes/rich-text.
  • Collaborative implementation of OT with Quill can be found at https://github.com/share/sharedb/tree/master/examples/rich-text.
  • For collaborative implementation with CRDT-yjs for Quill, refer to https://github.com/yjs/y-quill.

Draft.js

Draft is a framework for building rich text editors in React, providing a powerful API for creating and customizing text editors. It aims to be easy to extend and integrate with other libraries. Combining it with React allows developers to develop editors without directly manipulating the DOM or learning a separate UI building paradigm. Instead, they can directly write React components to implement the editor's UI. The overall concept of draft aligns very well with React. For example, it uses state management to store data structures, leverages the immutable.js library, delegates most data structure modifications to the browser's default behavior, and modifies rich text data through state management.

The README of draft describes the framework's design principles:

  • Scalable and customizable, providing building blocks to create various rich text editing experiences, from basic text styles to support for embedded media.

  • Declarative rich text, seamlessly integrating with React using a declarative API familiar to React users, abstracting the details of rendering, selection, and input behavior.

  • Immutable editor state. The draft model is built using immutable.js, providing an API for functional state updates and actively utilizing data persistence to achieve scalable memory usage.

Example

The data structure of a piece of text with emphasis is as follows.

{
  blocks: [
    {
      key: "3eesq",
      text: "This is an example of a text structure.",
      type: "unstyled",
      depth: 0,
      inlineStyleRanges: [
        { offset: 2, length: 4, style: "ITALIC" },
        { offset: 7, length: 4, style: "BOLD" },
      ],
      entityRanges: [],
      data: {},
    },
  ],
  entityMap: {},
};

Advantages

  • Flexible API for easily extending and customizing blocks and inline elements.
  • Leveraging React for the UI layer and data management, making it user-friendly for React users.
  • Relatively stable with minimal breaking changes, advantageous in terms of stability and detail handling.

Disadvantages

  • Heavily dependent on React as the UI layer, making it difficult to use with other UI frameworks.
  • Limited flexibility in the design of data structure models, especially when implementing nested structures such as tables.
  • While draft has been heavily optimized for performance, it still exhibits lag when rendering a large amount of content.

References

  • Official draft documentation: https://draftjs.org/docs/overview.
  • Official draft examples: https://draftjs.org/.
  • draft codesandbox example: https://codesandbox.io/s/github/gupta-piyush19/Draft-JS-Editor.

Every Day One Question

https://github.com/WindrunnerMax/EveryDay

References

https://github.com/hzjswlgbsj/blog https://zhuanlan.zhihu.com/p/425265438 https://jkrsp.com/slate-js-vs-draft-js/ https://www.zhihu.com/question/404836496 https://www.zhihu.com/question/449541344 https://juejin.cn/post/6844903838982340622 https://juejin.cn/post/7034480408888770567 https://juejin.cn/post/6974609015602937870 https://github.com/cudr/slate-collaborative https://blog.logrocket.com/what-is-slate-js-replace-quill-draft-js/ https://dev.to/charrondev/getting-to-know-quilljs---part-1-parchment-blots-and-lifecycle--3e76