Test for blocks within reusable blocks

WordPress has a handy function called has_block() that tests whether your page or post uses a particular block type. But it won’t look inside reusable blocks — that would be slower, and wouldn’t be necessary in most cases. But sometimes we do need to do that.

What the standard has_block() function does is look for the code signature of our block directly within the content using regular expressions. It’s quick, and for most purposes, it’s good enough. But sometimes we have blocks of some interest added to reusable blocks, that may require some extra things done to the page: enqueue / embed scripts or stylesheets, add a CSS class to the body, load some SVG sprites in the footer, etc. When that happens, we need to look inside any reusable blocks on the page too.

Doing that is more complicated. First, we can test to see if the page has any reusable blocks before we go any further. If it does, we need to parse the blocks on the page, and go spelunking through the hierarchy of blocks and testing each reusable block as if it’s another post — which, technically, it is, because that’s how WordPress stores reusable blocks.

That job of spelunking is easiest done with recursion. We test each block at the top of the hierarchy, and then for each inner block of those blocks, and so forth… it’s turtles all the way down.

/**
 * check post content for reusable block containing a block type
 */
function has_reusable_block_with_block(string $needle, $post = null) : bool {
    // quickly verify whether post has a reusable block
    if (!has_block('core/block', $post)) {
        return false;
    }

    // handle WP_Post | int | null for $post parameter
    if (!($post instanceof WP_Post)) {
        $post = get_post($post);
    }

    $content = get_post_field('post_content', $post);
    $blocks = parse_blocks($content);

    return _search_reusable_blocks($blocks, $needle);
}

/**
 * recurse through blocks, looking for a reusable block with the target block type
 * @internal
 */
function _search_reusable_blocks(array $blocks, string $needle) : bool {
    foreach ($blocks as $block) {
        // check for reusable block
        if ($block['blockName'] === 'core/block') {
            $id = $block['attrs']['ref'] ?? false;
            if ($id && has_block($needle, $id)) {
                return true;
            }
        }

        // check for nested blocks
        if (!empty($block['innerBlocks'])) {
            if (_search_reusable_blocks($block['innerBlocks'], $needle)) {
                return true;
            }
        }
    }

    return false;
}

As an example, I sometimes like to load a nice lightbox script for image galleries on websites. Here’s how I do that for both on-page galleries and galleries in reusable blocks:

if (has_block('gallery') || has_reusable_block_with_block('gallery')) {
    wp_enqueue_script('baguettebox');
    wp_enqueue_style('baguettebox');
    wp_enqueue_script('front-gallery');
}

Job is done, even when the sought-after block is inside a reusable block.