Skip to content

Block Toolbar: Add a Delete button for every block - #83549

Open
scruffian wants to merge 3 commits into
trunkfrom
scruffian/add/remove-block-toolbar-button
Open

scruffian wants to merge 3 commits into
trunkfrom
scruffian/add/remove-block-toolbar-button

Conversation

@scruffian

@scruffian scruffian commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

What?

Adds a Delete button to the block toolbar of every block, using the trash icon from @wordpress/icons. It sits in its own toolbar group immediately before the Options (⋮) menu.

Why?

I've done a lot of user testing over the last few weeks. One of the behaviours I have seen with almost all users is that they don't use the keyboard to delete blocks, but instead they use the mouse. This means that to delete a block they have to look for the action in the kebab menu. This is problematic for two reasons:

  1. Deleting is a common action, but we're making it require two interactions to achieve it. This is creates unnecessary when performing simple editing tasks
  2. It teaches users to expect to find common actions under the kebab menu, which often they aren't. We don't want to train our users to look under the kebab menu for every action as this makes the block toolbar obsolete.

How?

A new RemoveBlockButton component renders a ToolbarGroup with a single ToolbarButton, rendered from PrivateBlockToolbar just before BlockSettingsMenu:

  • The label is Delete for a single block and Delete %d blocks for a multi-selection.
  • It is always rendered and is disabled (still focusable, aria-disabled="true") when canRemoveBlocks() is false, which covers locked blocks, content-only locked children and preview mode.
  • Clicking dispatches removeBlocks( clientIds ), so selection moves to the previous block exactly as the Options → Delete menu item does.
  • The tooltip shows the keyboard shortcut for core/block-editor/remove, as the Options → Delete menu item and the Move up/Move down buttons in the same toolbar already do.

The existing "Delete" item in the Options menu is unchanged; the toolbar button is an additional route to the same action.

The toolbar is shared, so the extensible site editor (v2) picks this up in the same change.

Two notes for reviewers:

  • The component is .tsx; the test is .jsx with one entry in tools/eslint/suppressions.json for react/jsx-filename-extension, as in Block Editor: Use UI Menu for bindings and variation transforms #81925 and Test: Run rendered browser behavior in Vitest Browser Mode #80995. A .tsx test is not workable here: tsconfig.dev.base.json includes test and story files, so a .tsx test drags the src files it imports into that project and fails npm run typecheck with TS6307, since packages/block-editor/tsconfig.build.json is a progressive-typing allowlist that lists neither the component nor src/store/index.js. Note that this also means the new .tsx is not type-checked by any project yet, the same as global-styles/text-shadow-panel.tsx; adding it to the allowlist needs the store listed first, which belongs in its own PR.
  • keyboard-navigable-blocks.spec.js walks the toolbar with ArrowRight and needed a Delete step between More and Options. The two specs that assume Options is the last toolbar button still hold, since the new group sits before it. Four other specs do query a button named Delete, but none are affected: font-library.spec.js and template-registration.spec.js run on screens with no block toolbar, and block-removal.spec.js and block-notes.spec.js scope the query to a dialog.

Testing Instructions

  1. Create a new post or page and insert a paragraph.
  2. Confirm the block toolbar shows a trash icon between the block-specific controls and the Options (⋮) menu, with a "Delete" tooltip that also shows the delete keyboard shortcut.
  3. Click it and confirm the block is removed and selection moves to the previous block.
  4. Select several blocks. Confirm the tooltip reads "Delete N blocks", again with the shortcut, and that clicking removes all of them.
  5. Open the Options menu, choose Lock → "Prevent removal", and confirm the trash button is greyed out and does nothing when clicked.
  6. Insert a pattern, select a block inside it (content-only editing). Confirm the trash button is greyed out there too.
  7. Confirm the existing Options → Delete menu item still works.

Screenshots or screencast

The toolbar for a paragraph, with the new button before the Options menu:
Screenshot 2026-09-26 at 23 38 29

Use of AI Tools

This PR was authored with Claude Code (Claude Opus 5). Claude wrote the component, its unit tests and the e2e helper update; I reviewed the result and the manual verification in the editor described above.

@scruffian
scruffian requested a review from ellatrix as a code owner September 26, 2026 21:39
@github-actions github-actions Bot added the [Package] Block editor /packages/block-editor label Sep 26, 2026
@github-actions

github-actions Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

🎉 Props

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: scruffian <scruffian@git.wordpress.org>
Co-authored-by: draganescu <andraganescu@git.wordpress.org>
Co-authored-by: jasmussen <joen@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Updated as activity occurs, without notifying anyone named here. Add the props-bot label to refresh.

🏷️ Labels

This pull request needs exactly one label indicating its type, and has 0.

  • Required: any label starting with [Type].
  • Found: none.

Read more about Type labels in Gutenberg. If you cannot add labels yourself, a reviewer can do it for you.

📦 Bundle size

Size Change: +196 B (0%)

Total Size: 8.23 MB

📦 View Changed
Filename Size Change
build/scripts/block-editor/index.min.js 506 kB +196 B (+0.04%)

cdff095 Run

⚡ Performance

Show the results

Client side metrics exclude the server response time.

front-end-block-theme

Metric bf1ef33 trunk % Change
timeToFirstByte 56.4 ms +13.21% -2.66% 58.5 ms +8.38% -5.38% -3.59%
largestContentfulPaint 92 ms +13.04% -4.35% 98 ms +6.12% -6.12% -6.12%
lcpMinusTtfb 35.25 ms +18.16% -4.4% 39.6 ms +9.85% -16.04% -10.98%
wpBeforeTemplate 28.15 ms +17.19% -2.24% 29.05 ms +18.83% -4.37% -3.1%
wpTemplate 24.35 ms +5.34% -3.29% 24.57 ms +4.72% -4.97% -0.9%
wpTotal 52.59 ms +13.41% -2.53% 54.65 ms +8.29% -5.34% -3.77%
wpMemoryUsage 7.61 MB +0% -0% 7.57 MB +0% -0% 0.51%
wpDbQueries 17 +0% -0% 17 +0% -0% 0%

front-end-classic-theme

Metric bf1ef33 trunk % Change
timeToFirstByte 48.25 ms +6.11% -4.35% 46.5 ms +4.41% -0.97% 3.76%
largestContentfulPaint 104 ms +3.85% -3.85% 100 ms +4% -0% 4%
lcpMinusTtfb 54.7 ms +4.11% -1.55% 53.8 ms +1.39% -0.93% 1.67%
wpBeforeTemplate 26.34 ms +11.58% -2.32% 25.81 ms +1.9% -0.97% 2.05%
wpTemplate 17.95 ms +4.35% -2.56% 17.56 ms +1.94% -0.91% 2.22%
wpTotal 45 ms +6.36% -3.87% 43.56 ms +4.64% -0.9% 3.31%
wpMemoryUsage 6.22 MB +0% -0% 6.19 MB +0% -0% 0.58%
wpDbQueries 14 +0% -0% 14 +0% -0% 0%

media-processing

Metric bf1ef33 trunk % Change
mediaProcessingJpeg 394.47 ms +2.2% -0.35% 397.92 ms +0.97% -0.69% -0.87%
mediaProcessingAvif 6030.51 ms +0.15% -0.13% 6076.64 ms +0.23% -0.13% -0.76%
mediaProcessingJpegToAvif 4141.3 ms +0.31% -0.2% 4194.63 ms +0.43% -0.51% -1.27%

media-upload

Metric bf1ef33 trunk % Change
jpegUploadProcessing 1419.09 ms +37.03% -0.95% 1413.87 ms +0.9% -0.61% 0.37%
pngUploadProcessing 209.9 ms +8.99% -17.96% 180.89 ms +12.58% -1.4% 16.04%
largeJpegUploadProcessing 1407.96 ms +0.39% -0.64% 1416.1 ms +0.78% -0.67% -0.57%
multipleImageUploadProcessing 1547.65 ms +2.1% -0.33% 1569.71 ms +10.3% -0.29% -1.41%

post-editor

Metric bf1ef33 trunk % Change
serverResponse 416.37 ms +2.61% -7.39% 406.9 ms +5.7% -2.98% 2.33%
firstPaint 216.38 ms +9.52% -4.21% 262.1 ms +11.48% -23.79% -17.44%
domContentLoaded 970.09 ms +1.31% -1.3% 977.39 ms +1.96% -2.18% -0.75%
loaded 971.16 ms +1.31% -1.29% 978.52 ms +1.94% -2.17% -0.75%
firstContentfulPaint 419.26 ms +0.39% -0.49% 412.91 ms +2.44% -3.22% 1.54%
firstBlock 2830.04 ms +1.05% -2.6% 2815.63 ms +1.39% -1.45% 0.51%
type 16.89 ms +6.51% -2.07% 16.95 ms +4.54% -1.06% -0.35%
typeWithoutInspector 16.93 ms +5.02% -5.73% 16.7 ms +1.26% -1.8% 1.38%
typeWithTopToolbar 22.09 ms +1.58% -1.4% 22.91 ms +1.57% -4.32% -3.58%
typeContainer 8.21 ms +7.8% -3.41% 8.31 ms +3.01% -9.51% -1.2%
focus 67.84 ms +11.07% -4.11% 68.97 ms +11.83% -8.77% -1.64%
firstFocus 194.99 ms +0% -0% 190.72 ms +0% -0% 2.24%
selectAll 392.09 ms +0.96% -0.93% 419.7 ms +0.22% -1.72% -6.58%
listViewOpen 53.99 ms +6.69% -1.52% 56.74 ms +10.77% -3.61% -4.85%
inserterOpen 23.28 ms +5.2% -8.2% 22.88 ms +5.94% -5.64% 1.75%
inserterHover 1.81 ms +20.44% -9.39% 2.14 ms +7.01% -20.09% -15.42%
inserterSearch 7.92 ms +8.08% -4.17% 7.9 ms +2.91% -4.68% 0.25%
loadPatterns 608.01 ms +0.65% -6.06% 600.53 ms +1.5% -3.56% 1.25%
wpTotal 405.6 ms +2.57% -7.42% 396.72 ms +5.74% -2.98% 2.24%
wpMemoryUsage 13.14 MB +0% -0% 13.11 MB +0% -0% 0.28%
wpDbQueries 54 +0% -1.85% 54 +0% -0% 0%

site-editor

Metric bf1ef33 trunk % Change
serverResponse 491.38 ms +5.36% -3.87% 499.25 ms +4.97% -1.33% -1.58%
firstPaint 241.58 ms +23.06% -13.99% 232.16 ms +4.36% -4.88% 4.06%
domContentLoaded 1133.52 ms +0.41% -1.77% 1128.2 ms +0.8% -1.35% 0.47%
loaded 1134.8 ms +0.43% -1.79% 1129.34 ms +0.79% -1.34% 0.48%
firstContentfulPaint 453.21 ms +4.34% -4.52% 451.33 ms +1.22% -1.49% 0.42%
firstBlock 4147.12 ms +1.11% -0.65% 4127.29 ms +0.69% -0.6% 0.48%
type 17.04 ms +4.46% -3.87% 16.74 ms +7.11% -1.73% 1.79%
navigate 105.28 ms +6.63% -6.18% 113 ms +10.71% -9.72% -6.83%
loadPatterns 1311.63 ms +17.9% -8.06% 1300.01 ms +17.36% -5.1% 0.89%
loadPages 1045.54 ms +3.1% -0.68% 1055.09 ms +2.41% -1.42% -0.91%
wpTotal 482.15 ms +5.41% -3.95% 489.71 ms +4.98% -1.3% -1.54%
wpMemoryUsage 12.11 MB +0% -0% 12.09 MB +0% -0% 0.14%
wpDbQueries 43 +2.33% -0% 43.5 +1.15% -1.15% -1.15%

cdff095 Run

🏁 Flaky tests

Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

applies the filtered form to the Quick Edit DataForm in /test/e2e/specs/site-editor/view-config-extensibility.spec.js, passed after 1 failed attempt.
Error: apiRequestContext.fetch: socket hang up
Call log:
  - → PUT http://localhost:8889/wp-json/wp/v2/plugins/gutenberg-test-plugins/view-config-extensibility
    - user-agent: Playwright/1.63.0 (x64; ubuntu 24.04) node/24.18 CI/1
    - accept: */*
    - accept-encoding: gzip,deflate,br
    - X-WP-Nonce: cc3c366065
    - content-type: application/json
    - content-length: 21
    - cookie: wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_23778236db82f19306f247e20a353a99=admin%7C1790635528%7CJv2Yhhi1h7oxEtlNqLIWOEAcOl4SrpE9yScsA1jU5aC%7Cd14be0717e2a360bed8147ff83b106cd157d4634aff515173454be6e551fe77e; wp-settings-time-1=1790463554

    at RequestUtils.rest (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/rest.ts:112:39)
    at RequestUtils.deactivatePlugin (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/plugins.ts:89:13)
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/view-config-extensibility.spec.js:57:3

cdff095 Run

@scruffian scruffian self-assigned this Sep 26, 2026
@scruffian scruffian added the Needs Design Feedback Needs general design feedback. label Sep 26, 2026
Adds a trash button to the block toolbar, in its own toolbar group
immediately before the Options menu. It reads "Delete" for a single
block and "Delete N blocks" for a multi-selection, and is rendered
disabled (still focusable) when the selection cannot be removed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@scruffian
scruffian force-pushed the scruffian/add/remove-block-toolbar-button branch from b83b768 to d006b2c Compare September 26, 2026 22:11
scruffian and others added 2 commits September 26, 2026 23:20
The Options > Delete menu item and the Move up/Move down toolbar buttons
both surface their keyboard shortcut, so read the representation for
`core/block-editor/remove` and pass it to the new button too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`lint:changelogs` requires every unreleased entry to carry a Gutenberg
pull request link, which was failing the CHANGELOG structure check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@draganescu draganescu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2026-09-29 at 12 23 14

It appears on blocks that cannot be deleted such as pattern blocks.

@jasmussen

Copy link
Copy Markdown
Contributor

Nice. A PR is a good way to press/test the issue. And on the good side, I think this is going to be a popular button.

The main downside is: we are extremely pressed for space. Here's a nested list item block toolbar when it has a note, at our smallest breakpoint before mobile. I don't recall if we allow our block toolbar to scroll horizontally or not, but if we do, adding a trash button here will make it scroll. (And if we don't scroll, the ellipsis button will be cropped off.)

image

For that reason, the main reason I'm hesitant on this one is exactly that space issue: we can't keep growing the block toolbar like this, without eventually refactoring it in a way that handles the overflow. Is this the button that goes a bridge too far? Impossible to tell, but it has to be considered.

To clarify the block toolbar refactor I'm picturing: it should be similar to how the Google Docs toolbar elides as you resize the window, it simply pops off menu items into an overflow dropdown as the toolbar grows smaller. Not simple to implement, but almost certainly something we need. The question is when.

@scruffian

Copy link
Copy Markdown
Contributor Author

It appears on blocks that cannot be deleted such as pattern blocks.

It's disabled on these blocks. My thinking was that it's worth having it always there for consistency. I'd rather users could see the button and have it not work, than the button be missing and them not understand why. The disabled button should help communicate that this cannot be deleted, which a missing button does not.

This branch has not been deployed

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

Labels

Needs Design Feedback Needs general design feedback. [Package] Block editor /packages/block-editor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants