Richtext

Render a Rich Text editor field. Extends Base.

Interactive Demo
Example
const config = {
  components: {
    Example: {
      fields: {
        body: {
          type: "richtext",
        },
      },
      render: ({ body }) => body,
    },
  },
};

Params

ParamExampleTypeStatus
typetype: "richtext"”richtext”Required
contentEditablecontentEditable: falseBoolean-
initialHeightinitialHeight: 312Number | String-
optionsoptions: { bold: false }Object-
renderMenu()renderMenu: () => <RichTextMenu />Function-
renderInlineMenu()renderInlineMenu: () => <RichTextMenu />Function-
tiptap.extensionsextensions: []Array-
tiptap.selectorselector: (ctx) => ({ ... })Function-

Required params

type

The type of the field. Must be "richtext" for RichText fields.

const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
        },
      },
      // ...
    },
  },
};

Optional params

contentEditable

Enable inline text editing for this field. Defaults to false.

const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          contentEditable: true,
        },
      },
      // ...
    },
  },
};
Interactive Demo
Example

initialHeight

Set the initial height for the field input. Defaults to 192.

const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          initialHeight: 312,
        },
      },
      // ...
    },
  },
};

Does not apply to inline fields when using contentEditable.

options

The Rich Text field is built with Tiptap. Configure Puck’s included Tiptap extensions to modify editing behaviour.

const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          options: {
            // Disable an included extension
            bold: false,
 
            // Configure an included extension
            heading: { levels: [1, 2] },
          },
        },
      },
      // ...
    },
  },
};
Interactive Demo
Example

Included extensions

renderMenu(props)

Render a custom menu for the field. Use with the <RichTextMenu> component to arrange existing controls, or introduce new ones.

Receives Menu Render Props.

import { RichTextMenu } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          renderMenu: ({ children }) => (
            <RichTextMenu>
              {/* Render default menu */}
              {children}
 
              <RichTextMenu.Group>
                {/* Render a Blockquote control */}
                <RichTextMenu.Blockquote />
 
                {/* Render a custom control */}
                <RichTextMenu.Control {/* ... */} />
 
              </RichTextMenu.Group>
            </RichTextMenu>
          ),
        },
      },
      // ...
    },
  },
};
Interactive Demo
Example

renderInlineMenu(props)

Render a custom inline menu when using contentEditable. Use with the <RichTextMenu> component to arrange existing controls, or introduce new ones.

Receives Menu Render Props.

import { RichTextMenu } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          contentEditable: true,
          renderInlineMenu: ({ children }) => (
            <RichTextMenu>
              {/* Render default menu */}
              {children}
 
              <RichTextMenu.Group>
                {/* Render a Blockquote control */}
                <RichTextMenu.Blockquote />
 
                {/* Render a custom control */}
                <RichTextMenu.Control {/* ... */} />
 
              </RichTextMenu.Group>
            </RichTextMenu>
          ),
        },
      },
      // ...
    },
  },
};
Interactive Demo

tiptap.extensions

Provide custom Tiptap extensions to the editor to deeply customize behavior. See included extensions.

import Emoji from "@tiptap/extension-emoji";
 
const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          tiptap: {
            extensions: [Emoji],
          },
        },
      },
    },
  },
};

TipTap maintains an extensive library of extensions and documentation for developing your own.

tiptap.selector

Extend the default editor state to access additional editor information in the menu. See Tiptap docs.

import { RichTextMenu } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          tiptap: {
            selector: ({ editor, readOnly }) => ({
              isHighlight: editor?.isActive("highlight"),
              canHighlight:
                !readOnly && editor?.can().chain().toggleHighlight().run(),
            }),
          },
          renderMenu: ({ editorState }) => (
            <RichTextMenu>
              <RichTextMenu.Group>
                <button disabled={editorState?.canHighlight}>
                  {editorState?.isHighlight ? "Unhighlight" : "Highlight"}
                </button>
              </RichTextMenu.Group>
            </RichTextMenu>
          ),
        },
      },
    },
  },
};

Render props for renderMenu() and renderInlineMenu() APIs.

ParamExampleType
children<div />ReactNode
editor{}Editor | null
editorState{isBold: true}EditorState | null

children

The default menu items. Include this to extend the existing menu.

import { RichTextMenu } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          renderMenu: ({ children }) => (
            <RichTextMenu>
              {/* Render default menu */}
              {children}
 
              <RichTextMenu.Group>
                {/* Render a Blockquote control */}
                <RichTextMenu.Blockquote />
              </RichTextMenu.Group>
            </RichTextMenu>
          ),
        },
      },
      // ...
    },
  },
};

editor

The Tiptap Editor Instance. Use this to implement custom controls based on Puck’s included extensions, and additional extensions provided by tiptap.extensions.

import { RichTextMenu } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          renderMenu: ({ editor }) => (
            <RichTextMenu>
              <RichTextMenu.Group>
                <button
                  onClick={() =>
                    editor?.chain().focus().toggleBlockquote().run()
                  }
                >
                  Quote
                </button>
              </RichTextMenu.Group>
            </RichTextMenu>
          ),
        },
      },
      // ...
    },
  },
};

editorState

Access editor state information without causing re-renders. Provides the included state, and additional state provided by tiptap.selector.

import { RichTextMenu } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      fields: {
        description: {
          type: "richtext",
          renderMenu: ({ editorState }) => (
            <RichTextMenu>
              {editorState?.isBold ? "Bold" : "Not bold"}
            </RichTextMenu>
          ),
        },
      },
      // ...
    },
  },
};

Included state

  • isAlignLeft
  • canAlignLeft
  • isAlignCenter
  • canAlignCenter
  • isAlignRight
  • canAlignRight
  • isAlignJustify
  • canAlignJustify
  • isBold
  • canBold
  • isItalic
  • canItalic
  • isUnderline
  • canUnderline
  • isStrike
  • canStrike
  • isInlineCode
  • canInlineCode
  • isBulletList
  • canBulletList
  • isOrderedList
  • canOrderedList
  • isCodeBlock
  • canCodeBlock
  • isBlockquote
  • canBlockquote
  • canHorizontalRule

readOnly

Whether or not the field is currently read-only.