Display specific Gutenberg blocks of a post outside of the post content in the theme

Sometimes it could be useful to display parts of a post or page on another place on the site. For example, a slider above the title of the post/page. Here I show you how to loop through the blocks of a post block per block in a theme to get this done.

Usually, a theme uses the_content() to display the post content. More helpful for us is get_the_content(), to get the content of a post or page including the block comments of Gutenberg as it is stored in the database.

Since WordPress 5.0, core runs the do_blocks function on this content, that looks like that in 5.0.3:

/**
 * Parses dynamic blocks out of `post_content` and re-renders them.
 *
 * @since 5.0.0
 * @global WP_Post $post The post to edit.
 *
 * @param  string $content Post content.
 * @return string Updated post content.
 */
function do_blocks( $content ) {
	// If there are blocks in this content, we shouldn't run wpautop() on it later.
	$priority = has_filter( 'the_content', 'wpautop' );
	if ( false !== $priority && doing_filter( 'the_content' ) && has_blocks( $content ) ) {
		remove_filter( 'the_content', 'wpautop', $priority );
		add_filter( 'the_content', '_restore_wpautop_hook', $priority + 1 );
	}

	$blocks = parse_blocks( $content );
	$output = '';

	foreach ( $blocks as $block ) {
		$output .= render_block( $block );
	}

	return $output;
}

You can see that a parse_blocks() function is called, that returns the blocks of the content so that they can be accessed one by one in a loop. And this is the part we need.

Get the content blocks as an array

We can use the following line of code to get the blocks of a post as an array:

$blocks = parse_blocks( get_the_content() );

This is what an array entry of a paragraph block looks like:

2 => 
	array (size=5)
	'blockName' => string 'core/paragraph' (length=14)
	'attrs' => 
		array (size=0)
		empty
	'innerBlocks' => 
		array (size=0)
		empty
	'innerHTML' => string '<p>Ein Absatz</p>' (length=19)
	'innerContent' => 
		array (size=1)
		0 => string '<p>Ein Absatz</p>' (length=19)

The entry contains various information about the block, like the name and its content.

Display the slider block separate from the content

To display a slider from the content above the post title, we can do something like this:

// Get post content to extract slider shortcode.
$blocks = parse_blocks( get_the_content() );
foreach ( $blocks as $block ) {
	if ( 'soliloquy/soliloquywp' === $block['blockName'] ) {
		echo do_shortcode( $block['innerHTML'] );
		break;
	}
}
// Display the title.

The project where I had to create that behavior for uses the Soliloquy slider, which comes with a Gutenberg block (in the end it stores a shortcode in the database). The block has the block name soliloquy/soliloquywp, and that is the value we check for in the foreach loop.

After finding it, we display the Shortcode from $block['innerHTML'] and end the loop. Then we can display the title.

Displaying the rest of the content

Now we need to display the remaining content:

$content_markup = '';
foreach ( $blocks as $block ) {
	if ( 'soliloquy/soliloquywp' === $block['blockName'] ) {
		continue;
	} else {
		$content_markup .= render_block( $block );
	}
}

echo apply_filters( 'the_content', $content_markup );

We loop through the $blocks array again checking for the slider block. But this time to ignore that block in the output. For all other blocks, we add the return value of the render_block( $block ) call to a variable. The function returns the HTML string for a given block.

To correctly display things like embeds and shortcodes, we run all the the_content filters above $content_markup before displaying it.

This solution has the potential for optimization, so we could remove the core filters for block rendering to not run them twice. But if you — like me — want to use this special behavior of the content only in a few places and in all other cases simply call the_content(), you would need to modify those places, too.

If you do not have an enormous amount of blocks in your posts, the performance loss due to the double execution of the block parser logic probably will not be of much importance.

Related posts

8 comments on »Display specific Gutenberg blocks of a post outside of the post content in the theme«

  1. ceylan

    it is a very interesting article. However, a problem remains: it does not work for PHP views (dynamic blocks)...

    Reply
      1. ceylan

        $blocks = parse_blocks( get_the_content() );
        foreach ( $blocks as $block ) {
        if ( 'test/test' === $block['blockName'] ) {
        echo do_shortcode( $block['innerHTML'] );
        break;
        }
        }

        the test / test block is not displayed.

        print_r($blocks) :
        [...]
        Array ( [blockName] => test/test [attrs] => Array ( ) [innerBlocks] => Array ( ) [innerHTML] => [innerContent] => Array ( ) ) )

        Reply
  2. ceylan

    In fact it works with:

    $content_markup = '';
    $blocks = parse_blocks( get_the_content() );
    foreach ( $blocks as $block ) {
    if ( 'test/test' !== $block['blockName'] ) {
    continue;
    } else {
    $content_markup .= render_block( $block );
    }
    }

    echo apply_filters( 'the_content', $content_markup );

    Reply
    1. Florian Brinkmann

      Hey Ceylan,

      great to hear that you figured it out! Yes, the code in the article is meant for a block that stores a shortcode in the database, so it cannot be used exactly like that for other blocks.

      Best,
      Florian

      Reply

Leave a Comment

Your email address will not be published. Required fields are marked *