---
title: "Rendering and NodeViews"
description: "Customize Slot markup, NodeViews and view updates."
canonical_url: "https://tiptap.dev/docs/composable-docs/slots/api-reference/rendering"
---

# Rendering and NodeViews

Customize Slot markup, NodeViews and view updates.

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

## HTML attributes

```ts
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

| Attribute          | Value                   | Purpose                           |
| ------------------ | ----------------------- | --------------------------------- |
| `data-slot-kind`   | `'block'` or `'inline'` | Identifies the Node kind.         |
| `data-slot-id`     | Slot ID                 | Preserves identity.               |
| `data-slot-config` | Escaped JSON            | Preserves 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

| Attribute               | Value                  | Purpose                                |
| ----------------------- | ---------------------- | -------------------------------------- |
| `data-slot-empty`       | `'true'` or `'false'`  | Styles empty fields.                   |
| `data-slot-active`      | `'true'` or `'false'`  | Styles the active field.               |
| `data-slot-placeholder` | Configured placeholder | Supplies text when the field is empty. |

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

## Custom NodeViews

```ts
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](https://tiptap.dev/docs/editor/extensions/custom-extensions/node-views.md) for the framework APIs.

## Validation and permission updates

Use [`subscribeSlotViewUpdates`](https://tiptap.dev/docs/composable-docs/slots/api-reference/utilities.md#subscribeslotviewupdates) to refresh UI when attributes alone do not change.

```ts
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](https://tiptap.dev/docs/composable-docs/content-protection/api-reference/rendering.md). 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.
