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.

Update from January 9, 2020: As Jeslen pointed out in the comments, the wpautop filter should be removed from running on our content markup. Otherwise, we might get unexpected paragraph elements in our output. I updated the code accordingly.

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 );
	}
}

// Remove wpautop filter so we do not get paragraphs for two line breaks in the content.
$priority = has_filter( 'the_content', 'wpautop' );
if ( false !== $priority ) {
	remove_filter( 'the_content', 'wpautop', $priority );
}

echo apply_filters( 'the_content', $content_markup );

if ( false !== $priority ) {
	add_filter( 'the_content', 'wpautop', $priority );
}

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

20 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
  3. Jeslen

    Just a note, you may want to add the remove_filter/add_filter stuff for wpautop.

    In the example above, this is located in the first few lines of the do_blocks function. In the blocks.php core file that houses the do_blocks function, this is located at the bottom before the return statement.

    You might get some random tags thrown into your output without it.

    Reply
  4. Salvatore Savino

    Great! I could split text from "core/gallery" block, that are now separated.
    The only issue is that lighbox plugins that could integrate with the Gutenberg Gallery don't work when I apply this.
    I have tried with Simple Lightbox and Advanced Gutenberg (they both enable lighboxes for Gutenberg gallery block).
    Any ideas how to get them to work, together with this fix?

    Reply
      1. Salvatore Savino

        Hi Florian,
        I'm afraid I can't post links in here, maybe due to Akismet or some other spam filters, it's the 4th time I try to reply! 🙂
        Can I write you by mail or Fb?

        Reply
  5. Salvatore Savino

    Hi Florian,

    since I am not very familiar with programming, I used the code exactly as you showed here.
    The only differences are:

    1) block name "core/gallery" instead of "soliloquy/soliloquywp"
    2) "Displaying the rest of the content" code is being printed BEFORE the "Display the slider block separate from the content" code.
    3) $blocks = parse_blocks( get_the_content() ); is declared before "Displaying the rest of content"

    You can see here the result in my WIP website:
    https://www.creativenergy.it/portfolio/sports-mania/

    When I print with , Simple Lightbox plugin could apply the lightbox to the Gutenberg generated gallery. After that I print the filtered content, and then the splitted core/gallery. In this case the Simple Lightbox plugin can't apply the lightbox to it.

    Here is the php for this template:

    https://gist.github.com/Ksasa78/005e2aa5852bf7d29acb72f0cf02b33d

    Hope you can figure what's going on!

    Reply
      1. Salvatore Savino

        Grazie Florian!!! Now it works like a charm 🙂

        So you added:

        if ( function_exists('slb_activate') ) {
        $content = slb_activate($content);
        }

        right before printing the content, like in a manual plugin activation, I see.
        I will send soon a »tangible« thank you, since the whole explanation was really helpful with what I wanted to achieve with this template.

        Reply
        1. Florian Brinkmann

          Hi Salvatore,

          happy to hear that it works! 🙂

          Yes, that chunk of code is the important part to make the lightbox plugin work with markup that was generated by own functions.

          » I will send soon a »tangible« thank you, since the whole explanation was really helpful with what I wanted to achieve with this template.«

          Thanks a lot, it arrived ❤

          Best,
          Florian

          Reply

Leave a Comment

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