Rendering and NodeViews

Customize BlockSlot and InlineSlot as ordinary Tiptap Node extensions. Pass them to Slot; it retains the commands, indexing and validation behavior.

HTML attributes

Slot.configure({
  blockSlot: BlockSlot.configure({ HTMLAttributes: { class: 'block-field' } }),
  inlineSlot: InlineSlot.configure({ HTMLAttributes: { class: 'inline-field' } }),
})
  • HTMLAttributes (Record<string, string>): Outer-element attributes. Default: {}.
  • Required data-slot-* attributes take precedence over custom attributes.
  • The package supplies no toolbar, borders or colors. Style the wrapper in your application.

Serialized attributes

AttributeValuePurpose
data-slot-kind'block' or 'inline'Identifies the Node kind.
data-slot-idSlot IDPreserves identity.
data-slot-configEscaped JSONPreserves embedded configuration.

Default wrappers are div for block Slots and span for inline Slots. The accessible name uses config.label, falling back to the ID.

View-only attributes

AttributeValuePurpose
data-slot-empty'true' or 'false'Styles empty fields.
data-slot-active'true' or 'false'Styles the active field.
data-slot-placeholderConfigured placeholderSupplies text when the field is empty.

These attributes and placeholder presentation are not persisted in JSON, serialized HTML or clipboard content.

Custom NodeViews

const CustomBlockSlot = BlockSlot.extend({
  addNodeView() {
    return ReactNodeViewRenderer(MySlotView)
  },
})

Slot.configure({ blockSlot: CustomBlockSlot })

ReactNodeViewRenderer comes from @tiptap/react. JavaScript and Vue use their standard Tiptap NodeView APIs.

Contract

  • Preserve the canonical Node name, id/config attributes, content expression and defining/isolating behavior.
  • Register customized instances through Slot only. Instances are fixed at editor construction.
  • Provide one editable contentDOM, or the framework's NodeViewContent equivalent.
  • Let Tiptap render document content. Do not render a second copy from node.content.
  • Mark controls and labels noneditable. Make edits through commands.
  • Inline wrappers and content containers must use inline-compatible elements, such as span.
  • Custom views own visible labels and placeholders. Default placeholder text is not added to custom views.
  • Preserve selection, nested editing, IME and caret behavior. Do not swallow required content events or mutations.

See Tiptap NodeViews for the framework APIs.

Validation and permission updates

Use subscribeSlotViewUpdates to refresh UI when attributes alone do not change.

const unsubscribe = subscribeSlotViewUpdates({ editor, onUpdate: refresh })
// When disposing the view:
unsubscribe()
  • Read editor.storage.slot for the snapshot, active field and validation state.
  • Use getPos() and entry positions when imported IDs are ambiguous.
  • Use editor.can() for command availability and Content Protection queries for explanations.
  • Render only current validation results. Apply read permissions before displaying messages or content.
  • Mounting a view does not start validation.

HTML serialization

  • addNodeView controls the editor UI; renderHTML controls exported HTML.
  • Custom renderHTML must preserve the three serialized attributes and one content hole.
  • Generate attributes from the current Node with proper escaping.
  • Custom parseHTML must preserve IDs and complete configuration. Keep the default parser as a fallback.
  • Malformed configuration JSON in HTML becomes config: null and produces a validation issue.
  • UI controls, placeholders and validation messages belong to the NodeView, not exported content.

Redacted content

Content Protection replaces concealed Nodes with its own redaction renderer. Their original Slot NodeViews do not remain mounted.

A readable parent may contain concealed children. Its custom controls must not display those children's text, attributes or JSON elsewhere. The complete source document remains in client memory.