mui-tiptap

repository·main·Indexed 19 days ago

https://github.com/sjdemartini/mui-tiptap

A Material-UI (MUI) styled WYSIWYG rich text editor built on Tiptap and ProseMirror. It provides an all-in-one RichTextEditor component for quick setup, as well as modular components and providers for highly customized implementations. The library includes specialized extensions for resizable images, improved tables, font size management, and GitHub-like heading anchors, along with a suite of MUI-styled toolbar controls and a color picker.

Tokens
24.6K
Snippets
69
Records
85
Agent score
63%

What's inside mui-tiptap

  1. Configure Tiptap extension precedence and ordering

    main

    When providing an extensions array to <RichTextEditor extensions={[]} /> or useEditor({ extensions: [] }), the order of extensions matters because it determines keyboard shortcut and event precedence. Extensions that require higher precedence (to intercept events before other plugins) should be placed later in the array.

    • Tables: Place TableImproved or Table first in the array to ensure it has the lowest precedence (allowing other plugins to handle nested node interactions like list indentation).
    • Blockquote: Place Blockquote after Bold so that the Blockquote shortcut (e.g., Cmd+Shift+B) is not intercepted by the Bold shortcut (Cmd+B).
    • Mentions: Place Mention after list-related extensions (TaskList, BulletList, etc.) so that pressing Enter on a mention suggestion selects the mention instead of creating a new list item.
    // Example of extension ordering
    const extensions = [
      Table.configure({ ... }), // Lower precedence
      Bold,
      Blockquote,                // Higher precedence
      Mention,                   // Higher precedence
    ];
    
    // Usage
    <RichTextEditor extensions={extensions} />
  2. Use mui-tiptap utility classes for styling

    main

    mui-tiptap components include specific utility class names that can be used for external CSS or nested styling. For example, RichTextField uses:

    • MuiTiptap-RichTextField-root (root element)
    • MuiTiptap-RichTextField-[variant] (e.g., MuiTiptap-RichTextField-outlined)
    • MuiTiptap-RichTextField-content (inner content element)

    To avoid hard-coding these strings, import the corresponding class helper object from mui-tiptap.

    import { richTextFieldClasses } from "mui-tiptap";
    
    // Use the helper to access the class name
    const contentClass = richTextFieldClasses.content; // "MuiTiptap-RichTextField-content"
  3. Create and provide the editor yourself

    main

    For advanced customization, you can manage the Tiptap editor instance yourself using the @tiptap/react useEditor hook. To make the editor instance available to mui-tiptap components, wrap your component tree in a RichTextEditorProvider.

    Inside the provider, you can use:

    • RichTextField: A component that provides the editor area and accepts a controls prop for the menu bar. This is what RichTextEditor uses internally.
    • RichTextContent: A styled version of Tiptap's EditorContent for building completely custom UIs.
    import { useEditor } from "@tiptap/react";
    import StarterKit from "@tiptap/starter-kit";
    import {
      MenuButtonBold,
      MenuButtonItalic,
      MenuControlsContainer,
      MenuDivider,
      MenuSelectHeading,
      RichTextEditorProvider,
      RichTextField,
    } from "mui-tiptap";
    
    function App() {
      const editor = useEditor({
        extensions: [StarterKit],
        content: "<p>Hello <b style={{ fontWeight: 'bold' }}>world</b>!</p>",
        // With Tiptap v3, set shouldRerenderOnTransaction to true, as mui-tiptap currently relies
        // on rerenders to keep controls/children reactive to support both Tiptap v2 and v3.
        shouldRerenderOnTransaction: true,
      });
    
      return (
        <RichTextEditorProvider editor={editor}>
          <RichTextField
            controls={
              <MenuControlsContainer>
                <MenuSelectHeading />
                <MenuDivider />
                <MenuButtonBold />
                <MenuButtonItalic />
                {/* Add more controls of your choosing here */}
              </MenuControlsContainer>
            }
          />
        </RichTextEditorProvider>
      );
    }
  4. Manage editor state with RichTextEditorProvider

    main

    The RichTextEditorProvider uses React context to make the Tiptap editor instance available to all nested components. This prevents the need to manually pass the editor prop through multiple levels of your component tree. It is a required parent for most mui-tiptap components, except for the all-in-one RichTextEditor and RichTextReadOnly components.

    To access the editor in your own custom components, use the useRichTextEditorContext() hook.

  5. Customize mui-tiptap styles using the sx prop

    main

    You can apply styles to any mui-tiptap component using the standard MUI sx prop. This is useful for quick adjustments like background colors or margins.

    <RichTextEditor {...otherProps} sx={{ backgroundColor: "#222" }} />
  6. Customize mui-tiptap styles using MUI theme overrides

    main

    You can globally configure mui-tiptap components using the MUI createTheme API. Use the component name as the key in components (e.g., "MuiTiptap-FieldContainer") to define defaultProps, styleOverrides for specific slots, or custom variants based on props.

    const theme = createTheme({
      components: {
        // Override the behavior for the mui-tiptap `FieldContainer`
        "MuiTiptap-FieldContainer": {
          defaultProps: {
            // If no `variant` value is set, use "standard"
            variant: "standard",
          },
          styleOverrides: {
            // Apply this background color to the `root` slot
            root: { backgroundColor: "#222" },
            // Apply this border style to the `notchedOutline` slot
            notchedOutline: { borderStyle: "dashed" },
          },
          variants: [
            // When the `disabled` prop is `true`, show a red outline
            {
              props: { disabled: true },
              style: { outline: "1px solid red" },
            },
          ],
        },
      },
    });
  7. Localize mui-tiptap buttons and color pickers

    main

    You can override default labels for menu buttons and color pickers using the tooltipLabel and labels props.

    • General Buttons: Use tooltipLabel for the hover tooltip.
    • Color Pickers: Use the labels prop to override text in the color picker popper (e.g., cancelButton, saveButton, textFieldPlaceholder).
    <MenuButtonBold tooltipLabel="Toggle bold" />
    
    <MenuButtonTextColor
      tooltipLabel="Text color"
      labels={{
        cancelButton: "Cancel",
        removeColorButton: "Reset",
        removeColorButtonTooltipTitle: "Remove the color",
        saveButton: "OK",
        textFieldPlaceholder: 'Ex: "#7cb5ec"',
      }}
    />
  8. Localize mui-tiptap bubble menus

    main

    Bubble menus like LinkBubbleMenu and TableBubbleMenu can be localized using the labels prop to override the text of their action buttons.

    <LinkBubbleMenu
      labels={{
        viewLinkEditButtonLabel: "Edit link",
        viewLinkRemoveButtonLabel: "Remove link",
        editLinkAddTitle: "Add new link",
        editLinkEditTitle: "Update this link",
        editLinkCancelButtonLabel: "Cancel changes",
        editLinkTextInputLabel: "Text content",
        editLinkHrefInputLabel: "URL",
        editLinkSaveButtonLabel: "Save changes",
      }}
    />
    
    <TableBubbleMenu
      labels={{
        insertColumnBefore: "Add a new column before this",
        insertColumnAfter: "Add a new column after this",
        deleteColumn: "Remove current column",
      }}
    />
  9. Configure LinkBubbleMenu and TableBubbleMenu

    main

    Both LinkBubbleMenu and TableBubbleMenu are specialized bubble menus.

    If using RichTextEditor: Include these components via the RichTextEditor's children render-prop.

    If using custom editor setup (e.g., useEditor): Include the bubble menu as a child of the component where you call useEditor and render your RichTextField or RichTextContent. This ensures the bubble menu re-renders whenever the Tiptap editor forces an update, allowing it to position itself correctly.

  10. Install mui-tiptap

    main

    Install the mui-tiptap package using npm or yarn. The package has peer dependencies on @mui/material, @mui/icons-material, and various @tiptap/ packages. If you are using npm 7+ or pnpm, these should be installed automatically. For other package managers or if they are missing, install them manually.

    npm install mui-tiptap
    
    # or
    
    yarn add mui-tiptap
  11. Organize editor controls with MenuDivider and MenuControlsContainer

    main

    To create a structured and visually consistent toolbar for your editor, use MenuControlsContainer and MenuDivider:

    • MenuControlsContainer: Wraps your control components to provide consistent spacing between them.
    • MenuDivider: Renders a vertical line to separate different sections of your menu bar, helping to group related controls together.
    <MenuControlsContainer>
      <MenuSelectHeading />
      <MenuDivider />
      <MenuButtonBold />
      <MenuButtonItalic />
      {/* Add more controls of your choosing here */}
    </MenuControlsContainer>
  12. Implement drag-and-drop and paste for images

    main

    To support dragging and dropping image files or pasting them into the editor, provide handleDrop and handlePaste options within the editorProps of the RichTextEditor component or useEditor hook.

    For a convenient implementation, use the mui-tiptap insertImages utility to handle the file upload and insertion logic.

    // Conceptual implementation using editorProps
    <RichTextEditor
      editorProps={{
        handleDrop: (event, slice, emulated) => {
          // Use insertImages utility here to process files
          return true;
        },
        handlePaste: (event, slice, emulated) => {
          // Use insertImages utility here to process files
          return true;
        },
      }}
    />