Skip to content

Block assets are never enqueued when a block sits inside a nested synced pattern #2984

Description

@johansolve

Description

When a block sits inside a synced pattern (core/block) that is itself nested inside another block, none of that block's assets are enqueued on the frontend — neither its stylesheet nor its script. The block renders as bare markup.

I expect the pattern's blocks to get their assets registered regardless of how deeply the pattern is nested, the same way they do when the pattern sits at the top level of the page. Instead nothing is registered.

For the Form block the consequence is not only cosmetic: submission happens in JS, so the form cannot be submitted at all. .otter-form__container also loses display: flex, so field widths stop working and inputs fall back to the theme's default styling.

Step-by-step reproduction instructions

  1. Add a Form block to a page.
  2. Select it and create a synced pattern from it.
  3. On a page, place that pattern inside a Group block.
  4. View the page on the frontend and look for otter-form-style and build/blocks/form.js.

Two otherwise identical pages against the same pattern:

Page content otter-form-style form.js
core/block at top level loaded loaded
core/group > core/block missing missing

Screenshots, screen recording, code snippet or Help Scout ticket

Registration::enqueue_dependencies()inc/class-registration.php:531:

if ( has_block( 'core/block', $post ) ) {
    $blocks = parse_blocks( $content );
    $blocks = array_filter(
        $blocks,
        function ( $block ) {
            return 'core/block' === $block['blockName'] && isset( $block['attrs']['ref'] );
        }
    );

    foreach ( $blocks as $block ) {
        $this->enqueue_dependencies( $block['attrs']['ref'] );
    }
}

parse_blocks() returns a flat list of top-level blocks and innerBlocks is never walked, so a nested pattern is invisible. The has_block() guard above does match, since it searches the raw content string — only the collection step misses it.

Two things that may be worth more than adding recursion:

  1. The plugin already gets this right elsewhere. The dynamic CSS path in inc/css/class-block-frontend.php traverses nested patterns correctly. That asymmetry is what made this look like an oversight rather than a deliberate trade-off.

  2. The conditional registration is what defeats WordPress's own mechanism. The Form block declares "style": "otter-form-style" and "script": "otter-form" in its block.json, and core enqueues those handles when the block actually renders — at any depth, from a pattern or not. But the handles are only registered if this scan found the block first, so core's enqueue silently no-ops on an unregistered handle. Registration is cheap; enqueueing is what costs. Registering unconditionally and letting core enqueue at render time would make the scan, and this bug, unnecessary.

The same flat filter serves the widgets path (enqueue_dependencies( 'widgets' )), so a nested pattern in a widget area is affected too.

Environment info

Otter Blocks 3.2.2, WordPress 7.0.4, PHP 8.4, classic (non-FSE) theme — a Neve child theme. The code is unchanged on master as of this writing. No pastebin; happy to provide Site Health info if useful.

Is the issue you are reporting a regression

No. The pattern handling was added in 2.0.1 ("Fix styles not loaded for Reusable Blocks") and has only ever covered the top level. Since WP 6.3 made synced patterns the standard way to reuse content — typically nested inside a section or group — the case has become common.


Created with help of Claude Code.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions