JSPM

  • Created
  • Published
  • Downloads 43115
  • Score
    100M100P100Q150216F
  • License MIT

A Material-UI (MUI) styled WYSIWYG rich text editor, using Tiptap

Package Exports

  • mui-tiptap

Readme

mui-tiptap-logo

mui-tiptap: A customizable Material-UI (MUI) styled WYSIWYG rich text editor, using Tiptap.

npm mui-tiptap package npm type definitions GitHub Workflow Status

  • ✨ Styled based on your own MUI theme (colors, fonts, light vs dark mode, etc.)
  • 🛠️ Built on powerful Tiptap and ProseMirror foundations (extensible, real-time collaborative editing, cross-platform support, etc.)

Features:

  • 🧰 An all-in-one RichTextEditor component to get started immediately (no other components or hooks needed!), or individual modular components to customize to your needs
  • 😎 Built-in styles for Tiptap’s extensions (text formatting, lists, tables, Google Docs-like collaboration cursors; you name it!)
  • ▶️ Composable and extendable menu buttons and controls for the standard Tiptap extensions
  • 🖼️ ResizableImage extension for adding and resizing images directly in the editor
  • HeadingWithAnchor extension for dynamic GitHub-like anchor links for every heading you add
  • 🔗 LinkBubbleMenu so adding and editing links is a breeze
  • 🔳 TableImproved extension that fixes problems in the underlying Tiptap Table extension
  • 📝 TableBubbleMenu for interactively editing your rich text tables
  • 🏗️ General-purpose ControlledBubbleMenu for building your own custom menus, solving some shortcomings of the Tiptap BubbleMenu
  • And more!

Installation

npm install mui-tiptap

or

yarn add mui-tiptap

There are peer dependencies on @mui/material and @mui/icons-material (and their @emotion/ peers), react-icons, and @tiptap/ packages. These should be installed automatically by default if you’re using npm 7+ or pnpm. Otherwise, if your project doesn’t already use those, you can install them with:

npm install @mui/material @mui/icons-material @emotion/react @emotion/styled react-icons @tiptap/react @tiptap/extension-heading @tiptap/extension-image @tiptap/extension-link @tiptap/extension-table @tiptap/pm @tiptap/core

or

yarn add @mui/material @mui/icons-material @emotion/react @emotion/styled react-icons @tiptap/react @tiptap/extension-heading @tiptap/extension-image @tiptap/extension-link @tiptap/extension-table @tiptap/pm @tiptap/core

Get started

To use the all-in-one component:

import { Button } from "@mui/material";
import StarterKit from "@tiptap/starter-kit";
import { RichTextEditor, type RichTextEditorRef } from "mui-tiptap";
import { useRef } from "react";

function App() {
  const rteRef = useRef<RichTextEditorRef>(null);

  return (
    <div>
      <RichTextEditor
        ref={rteRef}
        content="<p>Hello world</p>"
        extensions={[StarterKit]} // Or any extensions you wish!
      />

      <Button onClick={() => console.log(rteRef.current?.editor?.getHTML())}>
        Show HTML
      </Button>
    </div>
  );
}

Tips and suggestions

Defining your editor extensions

  • Put the TableImproved (or Table) extension first in the array.

    • As noted in the underlying prosemirror-tables package, the table editing plugin should have the lowest precedence, since it depends on key and mouse events but other plugins will likely need to take handle those first. For instance, if you want to indent or dedent a list item inside a table, you should be able to do that by pressing tab, and tab should only move between table cells if not within such a nested node.
  • Put the Blockquote extension after the Bold extension, so Blockquote’s keyboard shortcut takes precedence.

    • Otherwise, the keyboard shortcut for Blockquote (Cmd+Shift+B) will mistakenly toggle the bold mark (due to its “overlapping” Cmd+b shortcut). (See related Tiptap issues here and here.)
  • If you'd like Subscript and Superscript extensions to be mutually exclusive, so that text can't be both superscript and subscript simultaneously, use the excludes configuration parameter to exclude each other.

    • As described in this Tiptap issue. For instance:

      const CustomSubscript = Subscript.extend({
        excludes: "superscript",
      });
      const CustomSuperscript = Superscript.extend({
        excludes: "subscript",
      });

(See Tiptap's general notes on extension plugin precedence and ordering here.)

🚧 More documentation coming soon!

Contributing

Get started here.