Skip to content

Dynamic blocks with useBlockProps() are double-wrapped in editor and do not receive .wp-block-* class #70490

Open
@pixelwatt

Description

@pixelwatt

Description

When creating a dynamic block (registered with a PHP render_callback and no JS save() function), applying useBlockProps() to the top-level element in edit() does not prevent double-wrapping in the block editor.

In this case:

  • The block is rendered inside a wrapper
    in the editor.
  • The developer’s own wrapper (the one using useBlockProps()) does not receive the expected .wp-block-[name] class.
  • This leads to inconsistency between editor and frontend.

This happens even when:

  • edit() returns a single root element
  • useBlockProps() is used correctly
  • BlockControls and Popover are moved outside the main return
  • A save() function is defined as () => null or () => <div {...useBlockProps.save()} />

What’s been tried:

  • Moving all controls outside the main wrapper
  • Adding a save() function that returns null
  • Adding a save() function with useBlockProps.save()
  • Avoiding any fragments or sibling return nodes

Impact:

This causes editor-styling bugs when relying on .wp-block-[name] selectors (especially with theme.json). It forces developers to write fragile CSS selectors or override unwanted inner wrapper behavior with layout hacks. It also makes using theme.json to set default styles untenable, as CSS is going to have to be provided for the backend anyway (since the auto-generated styles use a selector that isn't applied)

Suggested Fix Direction:

Ensure that when:
• useBlockProps() is returned as the top-level node,
• save() is null or present,
• and the structure is otherwise valid,

…the block is not wrapped again in a fallback .wp-block div.

Step-by-step reproduction instructions

  1. Register a dynamic block using register_block_type() with a PHP render_callback.
  2. In JS, define a block with an edit() function that returns a single <div {...useBlockProps()}>.
  3. Do not define a save() function (or define a null-returning one).
  4. Observe the rendered block in the editor DOM.

Expected:

...

Actual:

...

Screenshots, screen recording, code snippet

No response

Environment info

  • WordPress 6.8.1, Method theme (custom)
  • Chrome 137.0.7151.120, MacOS 15.4.1
  • Gutenberg plugin active or not — issue occurs in both
  • Block is fully dynamic (render_callback only)
  • useBlockProps() applied directly to return root
  • Global theme stylesheet (no per-block enqueued styles)

Please confirm that you have searched existing issues in the repo.

  • Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

  • Yes

Please confirm which theme type you used for testing.

  • Block
  • Classic
  • Hybrid (e.g. classic with theme.json)
  • Not sure

Metadata

Metadata

Assignees

No one assigned

    Labels

    [Status] Needs More InfoFollow-up required in order to be actionable.[Type] BugAn existing feature does not function as intended

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions