Skip to content

feat(placeholder): add excludedNodeTypes option to exclude wrapper nodes#7551

Open
clcl777 wants to merge 2 commits intoueberdosis:developfrom
clcl777:fix/placeholder-exclude-wrapper-nodes
Open

feat(placeholder): add excludedNodeTypes option to exclude wrapper nodes#7551
clcl777 wants to merge 2 commits intoueberdosis:developfrom
clcl777:fix/placeholder-exclude-wrapper-nodes

Conversation

@clcl777
Copy link

@clcl777 clcl777 commented Mar 1, 2026

Changes Overview

Add excludedNodeTypes option to the Placeholder extension to fix duplicate placeholders appearing on wrapper nodes when using includeChildren: true.

Problem

When using includeChildren: true with nested structures like lists, placeholders are shown on all empty descendant nodes, including wrapper nodes (bulletList, listItem) and content nodes (paragraph). This results in visually overlapping placeholders:

┌─────────────────────────────────┐
│ • Type something...             │  ← bulletList placeholder
│   ┌─────────────────────────┐   │
│   │ Type something...       │   │  ← listItem placeholder
│   │ ┌─────────────────────┐ │   │
│   │ │ Type something...   │ │   │  ← paragraph placeholder
│   │ └─────────────────────┘ │   │
│   └─────────────────────────┘   │
└─────────────────────────────────┘

This is visually confusing and not the intended behavior.

Solution

Add excludedNodeTypes option to allow excluding specific node types from showing placeholders:

Placeholder.configure({
  placeholder: 'Type something...',
  includeChildren: true,
  excludedNodeTypes: ['bulletList', 'orderedList', 'listItem'],
})

Now only the actual content nodes show placeholders:

┌─────────────────────────────────┐
│ •                               │  ← no placeholder (excluded)
│   ┌─────────────────────────┐   │
│   │                         │   │  ← no placeholder (excluded)
│   │ ┌─────────────────────┐ │   │
│   │ │ Type something...   │ │   │  ← placeholder shown
│   │ └─────────────────────┘ │   │
│   └─────────────────────────┘   │
└─────────────────────────────────┘

Implementation Approach

  • Added excludedNodeTypes: string[] option to PlaceholderOptions interface with a default value of empty array []
  • In the doc.descendants() callback, check if the current node type is in excludedNodeTypes
  • If excluded, skip placeholder decoration and continue traversing children based on includeChildren setting

Testing Done

Added 4 new test cases in packages/extensions/__tests__/placeholder.spec.ts:

  • Placeholder not shown on excluded node types
  • Placeholder shown on non-excluded nodes (paragraph)
  • Multiple node types can be excluded
  • includeChildren: false stops child traversal when node is excluded

Verification Steps

  1. Create an editor with nested list structure: <ul><li><p></p></li></ul>
  2. Configure Placeholder with includeChildren: true and excludedNodeTypes: ['bulletList', 'listItem']
  3. Verify that bulletList and listItem do not have data-placeholder attribute
  4. Verify that paragraph has data-placeholder attribute

Additional Notes

This fix maintains backward compatibility - the default behavior remains unchanged when excludedNodeTypes is not specified.

Checklist

  • I have created a changeset for this PR if necessary.
  • My changes do not break the library.
  • I have added tests where applicable.
  • I have followed the project guidelines.
  • I have fixed any lint issues.

Related Issues

steven-tey/novel#510

@changeset-bot
Copy link

changeset-bot bot commented Mar 1, 2026

🦋 Changeset detected

Latest commit: 7c8dc97

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 72 packages
Name Type
@tiptap/extensions Patch
@tiptap/starter-kit Patch
@tiptap/extension-character-count Patch
@tiptap/extension-dropcursor Patch
@tiptap/extension-focus Patch
@tiptap/extension-gapcursor Patch
@tiptap/extension-history Patch
@tiptap/extension-placeholder Patch
@tiptap/core Patch
@tiptap/extension-audio Patch
@tiptap/extension-blockquote Patch
@tiptap/extension-bold Patch
@tiptap/extension-bubble-menu Patch
@tiptap/extension-bullet-list Patch
@tiptap/extension-code-block-lowlight Patch
@tiptap/extension-code-block Patch
@tiptap/extension-code Patch
@tiptap/extension-collaboration-caret Patch
@tiptap/extension-collaboration Patch
@tiptap/extension-color Patch
@tiptap/extension-details Patch
@tiptap/extension-document Patch
@tiptap/extension-drag-handle-react Patch
@tiptap/extension-drag-handle-vue-2 Patch
@tiptap/extension-drag-handle-vue-3 Patch
@tiptap/extension-drag-handle Patch
@tiptap/extension-emoji Patch
@tiptap/extension-file-handler Patch
@tiptap/extension-floating-menu Patch
@tiptap/extension-font-family Patch
@tiptap/extension-hard-break Patch
@tiptap/extension-heading Patch
@tiptap/extension-highlight Patch
@tiptap/extension-horizontal-rule Patch
@tiptap/extension-image Patch
@tiptap/extension-invisible-characters Patch
@tiptap/extension-italic Patch
@tiptap/extension-link Patch
@tiptap/extension-list Patch
@tiptap/extension-mathematics Patch
@tiptap/extension-mention Patch
@tiptap/extension-node-range Patch
@tiptap/extension-ordered-list Patch
@tiptap/extension-paragraph Patch
@tiptap/extension-strike Patch
@tiptap/extension-subscript Patch
@tiptap/extension-superscript Patch
@tiptap/extension-table-of-contents Patch
@tiptap/extension-table Patch
@tiptap/extension-text-align Patch
@tiptap/extension-text-style Patch
@tiptap/extension-text Patch
@tiptap/extension-twitch Patch
@tiptap/extension-typography Patch
@tiptap/extension-underline Patch
@tiptap/extension-unique-id Patch
@tiptap/extension-youtube Patch
@tiptap/html Patch
@tiptap/markdown Patch
@tiptap/pm Patch
@tiptap/react Patch
@tiptap/static-renderer Patch
@tiptap/suggestion Patch
@tiptap/vue-2 Patch
@tiptap/vue-3 Patch
@tiptap/extension-list-item Patch
@tiptap/extension-list-keymap Patch
@tiptap/extension-table-cell Patch
@tiptap/extension-table-header Patch
@tiptap/extension-table-row Patch
@tiptap/extension-task-item Patch
@tiptap/extension-task-list Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify
Copy link

netlify bot commented Mar 1, 2026

Deploy Preview for tiptap-embed ready!

Name Link
🔨 Latest commit 7c8dc97
🔍 Latest deploy log https://app.netlify.com/projects/tiptap-embed/deploys/69a40ff9acbe3d00084c82bb
😎 Deploy Preview https://deploy-preview-7551--tiptap-embed.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@clcl777
Copy link
Author

clcl777 commented Mar 1, 2026

This is the document chage for this PR.

ueberdosis/tiptap-docs#646

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant