---
title: "Using NodeViews with Tracked Changes"
description: "Learn how to properly integrate NodeViews with Tracked Changes to enable suggestion tracking for custom components."
canonical_url: "https://tiptap.dev/docs/tracked-changes/guides/nodeview-support"
---

# Using NodeViews with Tracked Changes

Learn how to properly integrate NodeViews with Tracked Changes to enable suggestion tracking for custom components.

NodeViews allow you to render custom components for specific node types in your editor. When using NodeViews with Tracked Changes, you need to ensure that suggestion metadata is properly passed to your component's DOM elements.

## What are NodeViews?

NodeViews are custom React, Vue, or vanilla JavaScript components that replace the default rendering of specific node types. They're commonly used for:

- Images and media
- Tables
- Custom blocks or cards
- Embeds and interactive content
- Any node type that requires custom rendering logic

## Why NodeViews Need Special Handling

When Tracked Changes is enabled, the extension adds metadata to track modifications. For text content, this happens automatically through marks. However, for NodeViews, the suggestion metadata must be explicitly passed to your component.

Without proper handling, suggestion attributes won't reach your component's DOM, meaning:

- Suggestions won't be visually highlighted
- Suggestion queries may not find your NodeView changes
- The suggestion tracking will be incomplete

## The Problem: Missing Suggestion Attributes

Tracked Changes stores suggestion metadata directly on the node's attributes (`node.attrs`). Your NodeView needs to map those attrs to `data-suggestion-*` DOM attributes so the tracking system can find them.

**Incorrect (won't track suggestions):**

```jsx
function ImageNodeView({ node }) {
  return (
    <img src={node.attrs.src} alt={node.attrs.alt} />
  )
  // Problem: suggestion attrs from node.attrs not on the DOM — suggestions lost
}
```

## The Solution: getSuggestionHTMLAttributes()

The `getSuggestionHTMLAttributes()` utility reads suggestion data directly from the live ProseMirror `node` and returns the corresponding `data-suggestion-*` DOM attributes, safe to spread onto your component.

Pass `node`, not `HTMLAttributes`. The `HTMLAttributes` NodeView prop is computed once at NodeView creation and never updated, so it will be stale whenever suggestion state changes. The `node` prop always reflects the current state.

```jsx
import { getSuggestionHTMLAttributes } from '@tiptap-pro/extension-tracked-changes'

function ImageNodeView({ node }) {
  const suggestionAttrs = getSuggestionHTMLAttributes(node)

  return (
    <img
      src={node.attrs.src}
      alt={node.attrs.alt}
      {...suggestionAttrs}
    />
  )
}
```

## Implementation Examples

### Simple Image NodeView

```jsx
import { getSuggestionHTMLAttributes } from '@tiptap-pro/extension-tracked-changes'

function ImageNodeView({ node }) {
  const suggestionAttrs = getSuggestionHTMLAttributes(node)

  return (
    <figure {...suggestionAttrs}>
      <img
        src={node.attrs.src}
        alt={node.attrs.alt}
        className="editor-image"
      />
      {node.attrs.caption && <figcaption>{node.attrs.caption}</figcaption>}
    </figure>
  )
}
```

### Custom Block NodeView

```jsx
import { getSuggestionHTMLAttributes } from '@tiptap-pro/extension-tracked-changes'

function CustomBlockNodeView({ node }) {
  const suggestionAttrs = getSuggestionHTMLAttributes(node)

  return (
    <div
      {...suggestionAttrs}
      className="custom-block"
      style={{
        backgroundColor: node.attrs.bgColor,
        padding: '16px',
      }}
    >
      <h3>{node.attrs.title}</h3>
      <p>{node.attrs.description}</p>
    </div>
  )
}
```

### Table Cell with NodeView

```jsx
import { getSuggestionHTMLAttributes } from '@tiptap-pro/extension-tracked-changes'

function TableCellNodeView({ node }) {
  const suggestionAttrs = getSuggestionHTMLAttributes(node)

  return (
    <td {...suggestionAttrs}>
      {node.content}
    </td>
  )
}
```

## Best Practices

**Always use `getSuggestionHTMLAttributes(node)`.** Deriving the attributes from the node directly ensures they stay in sync as suggestion state changes — e.g. when a suggestion is accepted, rejected, or a remote update arrives via collaboration.

**Spread onto the root element.** Place suggestion attributes on the outermost DOM element of your NodeView so the entire component is tracked as a unit.

**Do not pass `HTMLAttributes` to the utility.** The `HTMLAttributes` NodeView prop is a snapshot computed when the NodeView is first created. It is not updated on subsequent renders, so using it would produce stale `data-suggestion-*` attributes after any state change.

**Test your implementation.** After implementing a NodeView, verify that:

- The component renders correctly with and without suggestions
- Suggestions visually highlight (with your CSS)
- Accepting/rejecting suggestions works as expected
- [Suggestion queries](https://tiptap.dev/docs/tracked-changes/api-reference/utilities.md) find your NodeView changes

## Querying NodeView Suggestions

Once properly integrated, you can query suggestions on NodeViews just like text suggestions:

```js
import { findSuggestions, getSuggestionAtSelection } from '@tiptap-pro/extension-tracked-changes'

// Find all suggestions (including those on NodeViews)
const all = findSuggestions(editor)

// Find suggestions at the current selection
const current = getSuggestionAtSelection(editor)

// Accept/reject suggestions on NodeViews
editor.commands.acceptSuggestion({ id: 'suggestion-123' })
editor.commands.rejectSuggestion({ id: 'suggestion-123' })
```

## See Also

- [getSuggestionHTMLAttributes() API reference](https://tiptap.dev/docs/tracked-changes/api-reference/utilities.md#getsuggestionsthtmlattributes)
- [Utilities](https://tiptap.dev/docs/tracked-changes/api-reference/utilities.md)
- [Commands](https://tiptap.dev/docs/tracked-changes/api-reference/commands.md)
